diff options
author | Emmanuel Bourg <ebourg@apache.org> | 2016-08-03 10:17:09 +0200 |
---|---|---|
committer | Emmanuel Bourg <ebourg@apache.org> | 2016-08-03 10:17:09 +0200 |
commit | 3d9b009255ffd897a9bd84ca3977cd3f553da8ef (patch) | |
tree | 13a8296e2989ff8da3fc50d335fd48b2dd0c6064 | |
parent | b2ec1a2d459cfef3ff13133c1f7f5972e3740258 (diff) |
Imported Upstream version 6.7.6
274 files changed, 25889 insertions, 3416 deletions
@@ -1,13 +1,118 @@ *.class +# Mobile Tools for Java (J2ME) +#.mtj.tmp/ +/target # Package Files # *.jar *.war *.ear +*.iml +.idea* + +### Maven template +target/ +pom.xml.tag +pom.xml.releaseBackup +pom.xml.versionsBackup +pom.xml.next +release.properties + + +### Eclipse template +*.pydevproject +.metadata +.gradle +bin/ +tmp/ +*.tmp +*.bak +*.swp +*~.nib +local.properties +.settings/ +.loadpath + +# External tool builders +.externalToolBuilders/ + +# Locally stored "Eclipse launch configurations" +*.launch + +# CDT-specific +.cproject + +# PDT-specific +.buildpath + +# sbteclipse plugin +.target + +# TeXlipse plugin +.texlipse + + +### JetBrains template +# Covers JetBrains IDEs: IntelliJ, RubyMine, PhpStorm, AppCode, PyCharm -# IntelliJ *.iml -.idea -# maven -target +## Directory-based project format: +.idea/ +# if you remove the above rule, at least ignore the following: + +# User-specific stuff: +# .idea/workspace.xml +# .idea/tasks.xml +# .idea/dictionaries + +# Sensitive or high-churn files: +# .idea/dataSources.ids +# .idea/dataSources.xml +# .idea/sqlDataSources.xml +# .idea/dynamic.xml +# .idea/uiDesigner.xml + +# Gradle: +# .idea/gradle.xml +# .idea/libraries + +# Mongo Explorer plugin: +# .idea/mongoSettings.xml + +## File-based project format: +*.ipr +*.iws + +## Plugin-specific files: + +# IntelliJ +out/ + +# mpeltonen/sbt-idea plugin +.idea_modules/ + +# JIRA plugin +atlassian-ide-plugin.xml + +# Crashlytics plugin (for Android Studio and IntelliJ) +com_crashlytics_export_strings.xml +crashlytics.properties +crashlytics-build.properties + + +### Java template +*.class + +# Mobile Tools for Java (J2ME) +.mtj.tmp/ + +# Package Files # +*.jar +*.war +*.ear + +# virtual machine crash logs, see http://www.java.com/en/download/help/error_hotspot.xml +hs_err_pid* + + @@ -0,0 +1,165 @@ + GNU LESSER GENERAL PUBLIC LICENSE + Version 3, 29 June 2007 + + Copyright (C) 2007 Free Software Foundation, Inc. <http://fsf.org/> + Everyone is permitted to copy and distribute verbatim copies + of this license document, but changing it is not allowed. + + + This version of the GNU Lesser General Public License incorporates +the terms and conditions of version 3 of the GNU General Public +License, supplemented by the additional permissions listed below. + + 0. Additional Definitions. + + As used herein, "this License" refers to version 3 of the GNU Lesser +General Public License, and the "GNU GPL" refers to version 3 of the GNU +General Public License. + + "The Library" refers to a covered work governed by this License, +other than an Application or a Combined Work as defined below. + + An "Application" is any work that makes use of an interface provided +by the Library, but which is not otherwise based on the Library. +Defining a subclass of a class defined by the Library is deemed a mode +of using an interface provided by the Library. + + A "Combined Work" is a work produced by combining or linking an +Application with the Library. The particular version of the Library +with which the Combined Work was made is also called the "Linked +Version". + + The "Minimal Corresponding Source" for a Combined Work means the +Corresponding Source for the Combined Work, excluding any source code +for portions of the Combined Work that, considered in isolation, are +based on the Application, and not on the Linked Version. + + The "Corresponding Application Code" for a Combined Work means the +object code and/or source code for the Application, including any data +and utility programs needed for reproducing the Combined Work from the +Application, but excluding the System Libraries of the Combined Work. + + 1. Exception to Section 3 of the GNU GPL. + + You may convey a covered work under sections 3 and 4 of this License +without being bound by section 3 of the GNU GPL. + + 2. Conveying Modified Versions. + + If you modify a copy of the Library, and, in your modifications, a +facility refers to a function or data to be supplied by an Application +that uses the facility (other than as an argument passed when the +facility is invoked), then you may convey a copy of the modified +version: + + a) under this License, provided that you make a good faith effort to + ensure that, in the event an Application does not supply the + function or data, the facility still operates, and performs + whatever part of its purpose remains meaningful, or + + b) under the GNU GPL, with none of the additional permissions of + this License applicable to that copy. + + 3. Object Code Incorporating Material from Library Header Files. + + The object code form of an Application may incorporate material from +a header file that is part of the Library. You may convey such object +code under terms of your choice, provided that, if the incorporated +material is not limited to numerical parameters, data structure +layouts and accessors, or small macros, inline functions and templates +(ten or fewer lines in length), you do both of the following: + + a) Give prominent notice with each copy of the object code that the + Library is used in it and that the Library and its use are + covered by this License. + + b) Accompany the object code with a copy of the GNU GPL and this license + document. + + 4. Combined Works. + + You may convey a Combined Work under terms of your choice that, +taken together, effectively do not restrict modification of the +portions of the Library contained in the Combined Work and reverse +engineering for debugging such modifications, if you also do each of +the following: + + a) Give prominent notice with each copy of the Combined Work that + the Library is used in it and that the Library and its use are + covered by this License. + + b) Accompany the Combined Work with a copy of the GNU GPL and this license + document. + + c) For a Combined Work that displays copyright notices during + execution, include the copyright notice for the Library among + these notices, as well as a reference directing the user to the + copies of the GNU GPL and this license document. + + d) Do one of the following: + + 0) Convey the Minimal Corresponding Source under the terms of this + License, and the Corresponding Application Code in a form + suitable for, and under terms that permit, the user to + recombine or relink the Application with a modified version of + the Linked Version to produce a modified Combined Work, in the + manner specified by section 6 of the GNU GPL for conveying + Corresponding Source. + + 1) Use a suitable shared library mechanism for linking with the + Library. A suitable mechanism is one that (a) uses at run time + a copy of the Library already present on the user's computer + system, and (b) will operate properly with a modified version + of the Library that is interface-compatible with the Linked + Version. + + e) Provide Installation Information, but only if you would otherwise + be required to provide such information under section 6 of the + GNU GPL, and only to the extent that such information is + necessary to install and execute a modified version of the + Combined Work produced by recombining or relinking the + Application with a modified version of the Linked Version. (If + you use option 4d0, the Installation Information must accompany + the Minimal Corresponding Source and Corresponding Application + Code. If you use option 4d1, you must provide the Installation + Information in the manner specified by section 6 of the GNU GPL + for conveying Corresponding Source.) + + 5. Combined Libraries. + + You may place library facilities that are a work based on the +Library side by side in a single library together with other library +facilities that are not Applications and are not covered by this +License, and convey such a combined library under terms of your +choice, if you do both of the following: + + a) Accompany the combined library with a copy of the same work based + on the Library, uncombined with any other library facilities, + conveyed under the terms of this License. + + b) Give prominent notice with the combined library that part of it + is a work based on the Library, and explaining where to find the + accompanying uncombined form of the same work. + + 6. Revised Versions of the GNU Lesser General Public License. + + The Free Software Foundation may publish revised and/or new versions +of the GNU Lesser General Public License from time to time. Such new +versions will be similar in spirit to the present version, but may +differ in detail to address new problems or concerns. + + Each version is given a distinguishing version number. If the +Library as you received it specifies that a certain numbered version +of the GNU Lesser General Public License "or any later version" +applies to it, you have the option of following the terms and +conditions either of that published version or of any later version +published by the Free Software Foundation. If the Library as you +received it does not specify a version number of the GNU Lesser +General Public License, you may choose any version of the GNU Lesser +General Public License ever published by the Free Software Foundation. + + If the Library as you received it specifies that a proxy can decide +whether future versions of the GNU Lesser General Public License shall +apply, that proxy's public statement of acceptance of any version is +permanent authorization for you to choose that version for the +Library.
\ No newline at end of file diff --git a/README.md b/README.md index 3aa5918..2e77718 100644..100755 --- a/README.md +++ b/README.md @@ -6,11 +6,14 @@ This module is available on maven central as <dependency> <groupId>net.openhft</groupId> <artifactId>lang</artifactId> - <version>6.1.1</version> + <version><!--replace with the latest version--></version> </dependency> The version 6.x signifies that it is build for Java 6+. (It requires Java 6 update 18 or later to build) +## JavaDoc +Check out our documentation at [JavaDoc] (http://openhft.github.io/Java-Lang/apidocs/) + ## Working with off heap objects. Java-Lang 6.1 adds support for basic off heap data structures. More collections types and more complex data types will be added in future versions. @@ -26,7 +29,7 @@ Java-Lang 6.1 adds support for basic off heap data structures. More collections // set data on dt array.recycle(dt); // recycle the reference (or discard it) - // create a ring buffer + // create a ring writeBuffer HugeQueue<DataType> queue = HugeCollections.newQueue(DataType.class, 10*1000*1000L); // give me a reference to an object to populate DataType dt2 = queue.offer(); @@ -51,11 +54,11 @@ Both classes provide functionality: * addAndGetInt and getAndAddInt operations ####Example - ByteBuffer byteBuffer = ByteBuffer.allocate(SIZE).order(ByteOrder.nativeOrder()); + ByteBuffer byteBuffer = ByteBuffer.allocate(SIZE); ByteBufferBytes bytes = new ByteBufferBytes(byteBuffer); - for (long i = 0; i < bytes.capacity(); i++) + for (long i = 0; i < bytes.maximumLimit(); i++) bytes.writeLong(i); - for (long i = bytes.capacity()-8; i >= 0; i -= 8) { + for (long i = bytes.maximumLimit()-8; i >= 0; i -= 8) { int j = bytes.readLong(i); assert i == j; } @@ -72,3 +75,4 @@ Unzip master.zip, Java-Lang-master folder will be extracted from zip. Now you have an eclipse project, import project into Eclipse If your Eclipse configuration is not UTF-8, after importing the project you may see some errors and strange characters in some .java files. To get rid of this problem change character enconding to UTF-8: project->properties->resource->text file encoding->utf8 + diff --git a/lang-integration/pom.xml b/lang-integration/pom.xml deleted file mode 100755 index a56b1a4..0000000 --- a/lang-integration/pom.xml +++ /dev/null @@ -1,125 +0,0 @@ -<?xml version="1.0" encoding="UTF-8"?> -<!-- - ~ Copyright 2013 Peter Lawrey - ~ - ~ Licensed under the Apache License, Version 2.0 (the "License"); - ~ you may not use this file except in compliance with the License. - ~ You may obtain a copy of the License at - ~ - ~ http://www.apache.org/licenses/LICENSE-2.0 - ~ - ~ Unless required by applicable law or agreed to in writing, software - ~ distributed under the License is distributed on an "AS IS" BASIS, - ~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - ~ See the License for the specific language governing permissions and - ~ limitations under the License. - --> - -<project xmlns="http://maven.apache.org/POM/4.0.0" - xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" - xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd"> - <modelVersion>4.0.0</modelVersion> - - <groupId>net.openhft</groupId> - <artifactId>lang-integration</artifactId> - <version>6.1.3</version> - <packaging>pom</packaging> - - <name>OpenHFT/Java-Lang/lang-osgi</name> - <description>Java Lang library for High Frequency Trading (Java 6+)</description> - - <properties> - <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding> - </properties> - - <dependencies> - <dependency> - <groupId>net.openhft</groupId> - <artifactId>lang</artifactId> - <version>${project.version}</version> - </dependency> - - <dependency> - <groupId>net.openhft</groupId> - <artifactId>affinity</artifactId> - <version>2.0.1</version> - <scope>test</scope> - </dependency> - - <dependency> - <groupId>junit</groupId> - <artifactId>junit</artifactId> - <version>4.11</version> - <scope>test</scope> - </dependency> - </dependencies> - - <build> - <testSourceDirectory>src/test/java</testSourceDirectory> - <plugins> - <plugin> - <groupId>org.apache.maven.plugins</groupId> - <artifactId>maven-compiler-plugin</artifactId> - <version>3.1</version> - <configuration> - <source>1.6</source> - <target>1.6</target> - <encoding>UTF-8</encoding> - </configuration> - </plugin> - <plugin> - <groupId>org.apache.maven.plugins</groupId> - <artifactId>maven-source-plugin</artifactId> - <version>2.2</version> - <executions> - <execution> - <id>attach-sources</id> - <phase>verify</phase> - <goals> - <goal>jar</goal> - </goals> - </execution> - </executions> - </plugin> - <plugin> - <groupId>org.apache.maven.plugins</groupId> - <artifactId>maven-javadoc-plugin</artifactId> - <version>2.9</version> - <executions> - <execution> - <id>attach-javadocs</id> - <phase>verify</phase> - <goals> - <goal>jar</goal> - </goals> - </execution> - </executions> - <configuration> - <show>public</show> - <nohelp>true</nohelp> - </configuration> - </plugin> - </plugins> - </build> - - <url>http://www.openhft.net</url> - <licenses> - <license> - <name>The Apache Software License, Version 2.0</name> - <url>http://www.apache.org/licenses/LICENSE-2.0.txt</url> - <distribution>repo</distribution> - <comments>A business-friendly OSS license</comments> - </license> - </licenses> - - <developers> - <developer> - <name>Peter Lawrey</name> - <email>peter.lawrey@higherfrequencytrading.com</email> - </developer> - </developers> - - <scm> - <url>scm:git:https://github.com/OpenHFT/Java-Lang.git</url> - </scm> -</project>
\ No newline at end of file diff --git a/lang-osgi/pom.xml b/lang-osgi/pom.xml deleted file mode 100755 index fd8123f..0000000 --- a/lang-osgi/pom.xml +++ /dev/null @@ -1,215 +0,0 @@ -<?xml version="1.0" encoding="UTF-8"?> -<!-- - ~ Copyright 2013 Peter Lawrey - ~ - ~ Licensed under the Apache License, Version 2.0 (the "License"); - ~ you may not use this file except in compliance with the License. - ~ You may obtain a copy of the License at - ~ - ~ http://www.apache.org/licenses/LICENSE-2.0 - ~ - ~ Unless required by applicable law or agreed to in writing, software - ~ distributed under the License is distributed on an "AS IS" BASIS, - ~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - ~ See the License for the specific language governing permissions and - ~ limitations under the License. - --> - -<project xmlns="http://maven.apache.org/POM/4.0.0" - xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" - xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd"> - <modelVersion>4.0.0</modelVersion> - - <groupId>net.openhft</groupId> - <artifactId>lang-osgi</artifactId> - <version>6.1.3</version> - <packaging>bundle</packaging> - - <name>OpenHFT/Java-Lang/lang-osgi</name> - <description>Java Lang library for High Frequency Trading (Java 6+)</description> - - <properties> - <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding> - <felix.version>3.2.2</felix.version> - <pax.exam.version>3.3.0</pax.exam.version> - <pax.url.version>1.6.0</pax.url.version> - </properties> - - <dependencies> - <dependency> - <groupId>org.kohsuke.jetbrains</groupId> - <artifactId>annotations</artifactId> - <version>9.0</version> - <scope>compile</scope> - </dependency> - - <dependency> - <groupId>net.openhft</groupId> - <artifactId>lang</artifactId> - <version>${project.version}</version> - </dependency> - - <dependency> - <groupId>junit</groupId> - <artifactId>junit</artifactId> - <version>4.11</version> - <scope>test</scope> - </dependency> - - - <!-- for OSGi testing --> - <dependency> - <groupId>org.ops4j.pax.exam</groupId> - <artifactId>pax-exam-container-native</artifactId> - <version>${pax.exam.version}</version> - <scope>test</scope> - </dependency> - <dependency> - <groupId>org.ops4j.pax.exam</groupId> - <artifactId>pax-exam-junit4</artifactId> - <version>${pax.exam.version}</version> - <scope>test</scope> - </dependency> - <dependency> - <groupId>org.ops4j.pax.exam</groupId> - <artifactId>pax-exam-link-mvn</artifactId> - <version>${pax.exam.version}</version> - <scope>test</scope> - </dependency> - <dependency> - <groupId>org.ops4j.pax.url</groupId> - <artifactId>pax-url-aether</artifactId> - <version>${pax.url.version}</version> - <scope>test</scope> - </dependency> - <dependency> - <groupId>org.apache.felix</groupId> - <artifactId>org.apache.felix.framework</artifactId> - <version>${felix.version}</version> - <scope>test</scope> - </dependency> - <dependency> - <groupId>ch.qos.logback</groupId> - <artifactId>logback-core</artifactId> - <version>0.9.6</version> - <scope>test</scope> - </dependency> - <dependency> - <groupId>ch.qos.logback</groupId> - <artifactId>logback-classic</artifactId> - <version>0.9.6</version> - <scope>test</scope> - </dependency> - - </dependencies> - - <build> - <testSourceDirectory>src/test/java</testSourceDirectory> - <plugins> - <plugin> - <groupId>org.apache.maven.plugins</groupId> - <artifactId>maven-compiler-plugin</artifactId> - <version>3.1</version> - <configuration> - <source>1.6</source> - <target>1.6</target> - <encoding>UTF-8</encoding> - </configuration> - </plugin> - <plugin> - <groupId>org.apache.maven.plugins</groupId> - <artifactId>maven-source-plugin</artifactId> - <version>2.2</version> - <executions> - <execution> - <id>attach-sources</id> - <phase>verify</phase> - <goals> - <goal>jar</goal> - </goals> - </execution> - </executions> - </plugin> - <plugin> - <groupId>org.apache.maven.plugins</groupId> - <artifactId>maven-javadoc-plugin</artifactId> - <version>2.9</version> - <executions> - <execution> - <id>attach-javadocs</id> - <phase>verify</phase> - <goals> - <goal>jar</goal> - </goals> - </execution> - </executions> - <configuration> - <show>public</show> - <nohelp>true</nohelp> - </configuration> - </plugin> - <!-- plugin> - <groupId>org.apache.maven.plugins</groupId> - <artifactId>maven-gpg-plugin</artifactId> - <version>1.4</version> - <configuration> - <passphrase>${gpg.passphrase}</passphrase> - </configuration> - <executions> - <execution> - <id>sign-artifacts</id> - <phase>verify</phase> - <goals> - <goal>sign</goal> - </goals> - </execution> - </executions> - </plugin --> - - <plugin> - <groupId>org.apache.felix</groupId> - <artifactId>maven-bundle-plugin</artifactId> - <version>2.3.7</version> - <extensions>true</extensions> - <configuration> - <instructions> - <Bundle-SymbolicName>${project.groupId}.${project.artifactId}</Bundle-SymbolicName> - <Bundle-Name>${project.artifactId}</Bundle-Name> - </instructions> - </configuration> - <executions> - <!-- - This execution makes sure that the manifest is available - when the tests are executed - --> - <execution> - <goals> - <goal>manifest</goal> - </goals> - </execution> - </executions> - </plugin> - </plugins> - </build> - - <url>http://www.openhft.net</url> - <licenses> - <license> - <name>The Apache Software License, Version 2.0</name> - <url>http://www.apache.org/licenses/LICENSE-2.0.txt</url> - <distribution>repo</distribution> - <comments>A business-friendly OSS license</comments> - </license> - </licenses> - - <developers> - <developer> - <name>Peter Lawrey</name> - <email>peter.lawrey@higherfrequencytrading.com</email> - </developer> - </developers> - - <scm> - <url>scm:git:https://github.com/OpenHFT/Java-Lang.git</url> - </scm> -</project>
\ No newline at end of file diff --git a/lang-osgi/src/test/java/net/openhft/lang/osgi/OSGiBundleTest.java b/lang-osgi/src/test/java/net/openhft/lang/osgi/OSGiBundleTest.java deleted file mode 100644 index d734312..0000000 --- a/lang-osgi/src/test/java/net/openhft/lang/osgi/OSGiBundleTest.java +++ /dev/null @@ -1,89 +0,0 @@ -/* - * Copyright 2013 Peter Lawrey - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package net.openhft.lang.osgi; - -import ch.qos.logback.classic.Level; -import ch.qos.logback.classic.Logger; -import org.junit.Test; -import org.junit.runner.RunWith; -import org.ops4j.pax.exam.Configuration; -import org.ops4j.pax.exam.Option; -import org.ops4j.pax.exam.junit.PaxExam; -import org.osgi.framework.Bundle; -import org.osgi.framework.BundleContext; -import org.slf4j.LoggerFactory; - -import javax.inject.Inject; -import java.io.File; - -import static org.junit.Assert.assertNotNull; -import static org.junit.Assert.assertTrue; -import static org.ops4j.pax.exam.CoreOptions.*; - -/** - * @author lburgazzoli - */ -@RunWith(PaxExam.class) -public class OSGiBundleTest { - @Inject - BundleContext context; - - @Configuration - public Option[] config() { - Logger root = (Logger) LoggerFactory.getLogger(Logger.ROOT_LOGGER_NAME); - root.setLevel(Level.INFO); - - return options( - systemProperty("org.osgi.framework.storage.clean").value("true"), - systemProperty("org.ops4j.pax.logging.DefaultServiceLog.level").value("WARN"), - mavenBundle("net.openhft", "compiler", "2.1"), - new File("Java-Lang/lang/target/classes").exists() ? bundle("reference:file:Java-Lang/lang/target/classes") : bundle("reference:file:../lang/target/classes"), - new File("Java-Lang/lang-osgi/target/classes").exists() ? bundle("reference:file:Java-Lang/lang-osgi/target/classes") : bundle("reference:file:target/classes"), - junitBundles(), - systemPackage("sun.misc"), - systemPackage("sun.nio.ch"), - systemPackage("com.sun.tools.javac.api"), - cleanCaches() - ); - } - - @Test - public void checkInject() { - assertNotNull(context); - } - - @Test - public void checkHelloBundle() { - Boolean bundleFound = false; - Boolean bundleActive = false; - - Bundle[] bundles = context.getBundles(); - for (Bundle bundle : bundles) { - if (bundle != null) { - if (bundle.getSymbolicName().equals("net.openhft.lang")) { - bundleFound = true; - if (bundle.getState() == Bundle.ACTIVE) { - bundleActive = true; - } - } - } - } - - assertTrue(bundleFound); - assertTrue(bundleActive); - } -} diff --git a/lang-osgi/src/test/java/net/openhft/lang/osgi/OSGiCollectionTest.java b/lang-osgi/src/test/java/net/openhft/lang/osgi/OSGiCollectionTest.java deleted file mode 100644 index 6cae71a..0000000 --- a/lang-osgi/src/test/java/net/openhft/lang/osgi/OSGiCollectionTest.java +++ /dev/null @@ -1,74 +0,0 @@ -/* - * Copyright 2013 Peter Lawrey - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package net.openhft.lang.osgi; - -import ch.qos.logback.classic.Level; -import ch.qos.logback.classic.Logger; -import net.openhft.lang.collection.HugeArray; -import net.openhft.lang.collection.HugeCollections; -import net.openhft.langosgi.model.JavaBeanInterface; -import org.junit.Ignore; -import org.junit.Test; -import org.junit.runner.RunWith; -import org.ops4j.pax.exam.Configuration; -import org.ops4j.pax.exam.Option; -import org.ops4j.pax.exam.junit.PaxExam; -import org.osgi.framework.BundleContext; -import org.slf4j.LoggerFactory; - -import javax.inject.Inject; -import java.io.File; - -import static org.junit.Assert.assertNotNull; -import static org.ops4j.pax.exam.CoreOptions.*; - -/** - * @author lburgazzoli - */ -@Ignore -@RunWith(PaxExam.class) -public class OSGiCollectionTest { - @Inject - BundleContext context; - - @Configuration - public Option[] config() { - Logger root = (Logger) LoggerFactory.getLogger(Logger.ROOT_LOGGER_NAME); - root.setLevel(Level.INFO); - - return options( - systemProperty("org.osgi.framework.storage.clean").value("true"), - systemProperty("org.ops4j.pax.logging.DefaultServiceLog.level").value("WARN"), - mavenBundle("net.openhft", "compiler", "2.1"), - new File("Java-Lang/lang/target/classes").exists() ? bundle("reference:file:Java-Lang/lang/target/classes") : bundle("reference:file:../lang/target/classes"), - new File("Java-Lang/lang-osgi/target/classes").exists() ? bundle("reference:file:Java-Lang/lang-osgi/target/classes") : bundle("reference:file:target/classes"), - junitBundles(), - systemPackage("sun.misc"), - systemPackage("sun.nio.ch"), - systemPackage("com.sun.tools.javac.api"), - cleanCaches() - ); - } - - @Test - public void checkHugeArray() { - int length = 10 * 1000 * 1000; - HugeArray<JavaBeanInterface> array = HugeCollections.newArray(JavaBeanInterface.class, length); - - assertNotNull(array); - } -} diff --git a/lang-sandbox/pom.xml b/lang-sandbox/pom.xml new file mode 100755 index 0000000..a293f4d --- /dev/null +++ b/lang-sandbox/pom.xml @@ -0,0 +1,96 @@ +<?xml version="1.0" encoding="UTF-8"?> +<!-- + ~ Copyright (C) 2015 higherfrequencytrading.com + ~ + ~ This program is free software: you can redistribute it and/or modify + ~ it under the terms of the GNU Lesser General Public License as published by + ~ the Free Software Foundation, either version 3 of the License. + ~ + ~ 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 Lesser General Public License for more details. + ~ + ~ You should have received a copy of the GNU Lesser General Public License + ~ along with this program. If not, see <http://www.gnu.org/licenses/>. + --> + +<project xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" + xmlns="http://maven.apache.org/POM/4.0.0" + xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd"> + + <parent> + <groupId>net.openhft</groupId> + <artifactId>java-parent-pom</artifactId> + <version>1.1.1</version> + <relativePath/> + </parent> + + <name>OpenHFT/Java-Lang/lang-sandbox</name> + <modelVersion>4.0.0</modelVersion> + <artifactId>lang-sandbox</artifactId> + <version>6.7.1-SNAPSHOT</version> + + <dependencyManagement> + <dependencies> + <dependency> + <groupId>net.openhft</groupId> + <artifactId>third-party-bom</artifactId> + <type>pom</type> + <version>3.4.20</version> + <scope>import</scope> + </dependency> + <dependency> + <groupId>net.openhft</groupId> + <artifactId>lang</artifactId> + <version>${project.version}</version> + </dependency> + </dependencies> + </dependencyManagement> + + <dependencies> + + <dependency> + <groupId>org.slf4j</groupId> + <artifactId>slf4j-api</artifactId> + </dependency> + + <dependency> + <groupId>net.openhft</groupId> + <artifactId>lang</artifactId> + </dependency> + + <dependency> + <groupId>com.sun.java</groupId> + <artifactId>tools</artifactId> + </dependency> + + <dependency> + <groupId>junit</groupId> + <artifactId>junit</artifactId> + <scope>test</scope> + </dependency> + + <dependency> + <groupId>org.slf4j</groupId> + <artifactId>slf4j-simple</artifactId> + <scope>test</scope> + </dependency> + + </dependencies> + <build> + <plugins> + <plugin> + <groupId>org.apache.maven.plugins</groupId> + <artifactId>maven-compiler-plugin</artifactId> + <configuration> + <compilerArgument>-Xlint:deprecation</compilerArgument> + <source>1.8</source> + <target>1.8</target> + <encoding>UTF-8</encoding> + </configuration> + </plugin> + </plugins> + </build> + +</project> diff --git a/lang-sandbox/src/main/java/net/openhft/lang/WaitStrategy.java b/lang-sandbox/src/main/java/net/openhft/lang/WaitStrategy.java new file mode 100644 index 0000000..b53151c --- /dev/null +++ b/lang-sandbox/src/main/java/net/openhft/lang/WaitStrategy.java @@ -0,0 +1,25 @@ +/* + * Copyright 2014 Higher Frequency Trading + * + * http://www.higherfrequencytrading.com + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package net.openhft.lang; + +/* + * JLANGX-10 Wait strategy + */ +public interface WaitStrategy { + public void await(int counter); +} diff --git a/lang-sandbox/src/main/java/net/openhft/lang/WaitStrategyBuilder.java b/lang-sandbox/src/main/java/net/openhft/lang/WaitStrategyBuilder.java new file mode 100644 index 0000000..d92e75a --- /dev/null +++ b/lang-sandbox/src/main/java/net/openhft/lang/WaitStrategyBuilder.java @@ -0,0 +1,190 @@ +/* + * Copyright 2014 Higher Frequency Trading + * + * http://www.higherfrequencytrading.com + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package net.openhft.lang; + +import java.util.LinkedList; +import java.util.List; + +public class WaitStrategyBuilder { + private int lowerLimit; + private List<WaiterHelper> waiters; + + public WaitStrategyBuilder() { + this.waiters = new LinkedList<WaiterHelper>(); + this.lowerLimit = 0; + } + + // ************************************************************************* + // + // ************************************************************************* + + public WaitStrategyBuilder busyWaiter(int count) { + return waiter(Waiters.BUSY_WAITER,count); + } + + public WaitStrategyBuilder yeldWaiter(int count) { + return waiter(Waiters.YELD_WAITER,count); + } + + public WaitStrategyBuilder parkWaiter(int count) { + return waiter(Waiters.PARK_WAITER,count); + } + + public WaitStrategyBuilder waiter(Waiter waiter,int count) { + this.waiters.add(new WaiterHelper(waiter,this.lowerLimit,count)); + this.lowerLimit += count; + return this; + } + + public WaitStrategy build() { + switch(this.waiters.size()) { + case 1: + return new WaitStrategy1(this.waiters); + case 2: + return new WaitStrategy2(this.waiters); + case 3: + return new WaitStrategy3(this.waiters); + } + + return new WaitStrategyN(this.waiters); + } + + // ************************************************************************* + // + // ************************************************************************* + + private class WaiterHelper { + private final Waiter waiter; + private int min; + private int max; + + public WaiterHelper(final Waiter waiter) { + this(waiter,-1,-1); + } + + public WaiterHelper(final Waiter waiter,int start, int count) { + this.waiter = waiter; + this.min = start; + this.max = start + count; + } + + public boolean contains(int value) { + return value >= this.min && value <= this.min; + } + + public Waiter waiter() { + return this.waiter; + } + + public int min() { + return this.min; + } + + public int max() { + return this.max; + } + } + + // ************************************************************************* + // + // ************************************************************************* + + private class WaitStrategy1 implements WaitStrategy { + private final Waiter waiter; + + public WaitStrategy1(final List<WaiterHelper> waiters) { + this.waiter = waiters.get(0).waiter; + } + + @Override + public void await(int counter) { + this.waiter.await(); + } + } + + private class WaitStrategy2 implements WaitStrategy { + private final int limit; + private final Waiter waiter1; + private final Waiter waiter2; + + public WaitStrategy2(final List<WaiterHelper> waiters) { + this.limit = waiters.get(0).max; + this.waiter1 = waiters.get(0).waiter; + this.waiter2 = waiters.get(1).waiter; + } + + @Override + public void await(int counter) { + if(counter < this.limit) { + this.waiter1.await(); + + } else { + this.waiter2.await(); + } + } + } + + private class WaitStrategy3 implements WaitStrategy { + private final int limit1; + private final int limit2; + private final Waiter waiter1; + private final Waiter waiter2; + private final Waiter waiter3; + + public WaitStrategy3(final List<WaiterHelper> waiters) { + this.limit1 = waiters.get(0).max; + this.limit2 = waiters.get(1).max; + this.waiter1 = waiters.get(0).waiter; + this.waiter2 = waiters.get(1).waiter; + this.waiter3 = waiters.get(2).waiter; + } + + @Override + public void await(int counter) { + if(counter < this.limit1) { + this.waiter1.await(); + + } else if(counter < this.limit2) { + this.waiter2.await(); + + } else { + this.waiter3.await(); + } + } + } + + private class WaitStrategyN implements WaitStrategy { + final List<WaiterHelper> waiters; + + public WaitStrategyN(final List<WaiterHelper> waiters) { + this.waiters = new LinkedList<WaiterHelper>(waiters); + } + + @Override + public void await(int counter) { + WaiterHelper wh = null; + for(int i=0;i<this.waiters.size();i++) { + wh = this.waiters.get(i); + if(i == this.waiters.size() - 1 || wh.contains(counter)) { + wh.waiter().await(); + break; + } + } + } + } +} diff --git a/lang-sandbox/src/main/java/net/openhft/lang/Waiter.java b/lang-sandbox/src/main/java/net/openhft/lang/Waiter.java new file mode 100644 index 0000000..0275376 --- /dev/null +++ b/lang-sandbox/src/main/java/net/openhft/lang/Waiter.java @@ -0,0 +1,22 @@ +/* + * Copyright 2014 Higher Frequency Trading + * + * http://www.higherfrequencytrading.com + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package net.openhft.lang; + +public interface Waiter { + public void await(); +} diff --git a/lang-sandbox/src/main/java/net/openhft/lang/Waiters.java b/lang-sandbox/src/main/java/net/openhft/lang/Waiters.java new file mode 100644 index 0000000..4dd73e2 --- /dev/null +++ b/lang-sandbox/src/main/java/net/openhft/lang/Waiters.java @@ -0,0 +1,46 @@ +/* + * Copyright 2014 Higher Frequency Trading + * + * http://www.higherfrequencytrading.com + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package net.openhft.lang; + +import java.util.concurrent.locks.LockSupport; + +public class Waiters { + public static final Waiter BUSY_WAITER = new BusyWaiter(); + public static final Waiter YELD_WAITER = new YieldWaiter(); + public static final Waiter PARK_WAITER = new ParkWaiter(); + + public static final class BusyWaiter implements Waiter { + @Override + public void await() { + } + } + + public static final class YieldWaiter implements Waiter { + @Override + public void await() { + Thread.yield(); + } + } + + public static final class ParkWaiter implements Waiter { + @Override + public void await() { + LockSupport.parkNanos(1L); + } + } +} diff --git a/lang-sandbox/src/main/java/net/openhft/lang/arena/Arena.java b/lang-sandbox/src/main/java/net/openhft/lang/arena/Arena.java new file mode 100755 index 0000000..9b76bc4 --- /dev/null +++ b/lang-sandbox/src/main/java/net/openhft/lang/arena/Arena.java @@ -0,0 +1,74 @@ +/* + * Copyright 2014 Higher Frequency Trading + * + * http://www.higherfrequencytrading.com + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package net.openhft.lang.arena; + +import net.openhft.lang.io.Bytes; +import net.openhft.lang.io.NativeBytes; +import net.openhft.lang.io.OffHeapLock; + +/** + * Created by peter.lawrey on 20/06/14. + */ +public interface Arena { + enum Mode { + SINGLE_BLOCK, CONTINUOUS_BLOCKS, LINKED_BLOCKS + } + + Mode getMode(); + + OffHeapLock readLock(); + + OffHeapLock writeLock(); + + // log2(allocation multiple) + int blockSizeBits(); + + // allocate a block of memory + int /* handle */ allocate(int hash, int sizeInBlocks); + + // resize a block of memory + int /* new handle */ reallocate(int hash, int handle, int sizeInBlocks); + + // free a block of memory + void free(int hash, int handle, int sizeInBlocks); + + // read to data stored. + void copyTo(Bytes bytes, int handle); + + // read from data stored + void copyFrom(int handle, Bytes bytes); + + // map the data directly. + void setBytes(int handle, NativeBytes bytes) throws IllegalStateException; + + // start a hash lookup + int firstHandleFor(int hash); + + // next hash or 0 + int nextHandle(); + + // metrics + int handlesUsed(); + + int handlesCapacity(); + + int entriesUsed(); + + int entriesCapacity(); +} diff --git a/lang-sandbox/src/main/java/net/openhft/lang/arena/MappedArena.java b/lang-sandbox/src/main/java/net/openhft/lang/arena/MappedArena.java new file mode 100755 index 0000000..cbd3223 --- /dev/null +++ b/lang-sandbox/src/main/java/net/openhft/lang/arena/MappedArena.java @@ -0,0 +1,134 @@ +/* + * Copyright 2014 Higher Frequency Trading + * + * http://www.higherfrequencytrading.com + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package net.openhft.lang.arena; + +import net.openhft.lang.io.*; +import net.openhft.lang.io.serialization.ObjectSerializer; + +import java.io.File; +import java.io.IOException; +import java.nio.channels.FileChannel; + +/** + * Created by peter.lawrey on 22/06/14. + */ +public class MappedArena implements Arena { + static final byte[] MAGIC = "Arena001".getBytes(); + static final int MAGIC_OFFSET = 0; + static final int LOCK_OFFSET = MAGIC_OFFSET + 8; + static final int ALLOCATE_SIZE_OFFSET = LOCK_OFFSET + 8; + static final int ALLOCATIONS_OFFSET = ALLOCATE_SIZE_OFFSET + 4; + static final int HEADER_ID_OFFSET0 = 32; + static final int HEADER_ID_SIZE = 4; + static final int HEADER_LENGTH = 64; + + final MappedArenaStores stores; + private final Mode mode; + private final OffHeapLock readLock; + private final OffHeapLock writeLock; + private final DirectBytes header; + + /** + * The lock consists of the reading/writing mode, writer count, writers waiting, reader count, readers waiting + */ + public MappedArena(File file, long minSize, ObjectSerializer objectSerializer, Mode mode) throws IOException { + this.mode = mode; + stores = new MappedArenaStores(file, FileChannel.MapMode.READ_WRITE, minSize, objectSerializer); + header = stores.acquire(0, HEADER_LENGTH); + OffHeapReadWriteLock ohrwl = new OffHeapReadWriteLock(header, LOCK_OFFSET); + readLock = ohrwl.readLock(); + writeLock = ohrwl.writeLock(); + } + + @Override + public Mode getMode() { + return mode; + } + + @Override + public OffHeapLock readLock() { + return readLock; + } + + @Override + public OffHeapLock writeLock() { + return writeLock; + } + + @Override + public int blockSizeBits() { + return 0; + } + + @Override + public int allocate(int hash, int sizeInBlocks) { + return 0; + } + + @Override + public int reallocate(int hash, int handle, int sizeInBlocks) { + return 0; + } + + @Override + public void free(int hash, int handle, int sizeInBlocks) { + } + + @Override + public void copyTo(Bytes bytes, int handle) { + } + + @Override + public void copyFrom(int handle, Bytes bytes) { + } + + @Override + public void setBytes(int handle, NativeBytes bytes) throws IllegalStateException { + } + + @Override + public int firstHandleFor(int hash) { + return 0; + } + + @Override + public int nextHandle() { + return 0; + } + + @Override + public int handlesUsed() { + return 0; + } + + @Override + public int handlesCapacity() { + return 0; + } + + @Override + public int entriesUsed() { + return 0; + } + + @Override + public int entriesCapacity() { + return 0; + } +} diff --git a/lang-sandbox/src/main/java/net/openhft/lang/arena/MappedArenaStores.java b/lang-sandbox/src/main/java/net/openhft/lang/arena/MappedArenaStores.java new file mode 100644 index 0000000..7918aa0 --- /dev/null +++ b/lang-sandbox/src/main/java/net/openhft/lang/arena/MappedArenaStores.java @@ -0,0 +1,225 @@ +/* + * Copyright 2014 Higher Frequency Trading + * + * http://www.higherfrequencytrading.com + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package net.openhft.lang.arena; + +import net.openhft.lang.io.BytesStore; +import net.openhft.lang.io.DirectBytes; +import net.openhft.lang.io.serialization.ObjectSerializer; +import net.openhft.lang.model.constraints.NotNull; +import sun.misc.Cleaner; +import sun.nio.ch.FileChannelImpl; + +import java.io.Closeable; +import java.io.File; +import java.io.IOException; +import java.io.RandomAccessFile; +import java.lang.reflect.InvocationTargetException; +import java.lang.reflect.Method; +import java.nio.channels.FileChannel; +import java.util.ArrayList; +import java.util.List; +import java.util.concurrent.atomic.AtomicInteger; + +public class MappedArenaStores implements Closeable { + + private static final int MAP_RO = 0; + private static final int MAP_RW = 1; + private static final int MAP_PV = 2; + + // retain to prevent GC. + private final File file; + private final RandomAccessFile randomAccessFile; + private final FileChannel fileChannel; + private final FileChannel.MapMode mode; + private final List<MappedArenaStore> storeList = new ArrayList<MappedArenaStore>(); + private final ObjectSerializer objectSerializer; + + public MappedArenaStores(File file, FileChannel.MapMode mode, long minSize, ObjectSerializer objectSerializer) throws IOException { + if (minSize < 0 || minSize > 128L << 40) { + throw new IllegalArgumentException("invalid minSize: " + minSize); + } + + this.file = file; + this.objectSerializer = objectSerializer; + + try { + randomAccessFile = new RandomAccessFile(file, accessModeFor(mode)); + this.mode = mode; + this.fileChannel = randomAccessFile.getChannel(); + storeList.add(new MappedArenaStore(0, minSize)); + } catch (Exception e) { + throw wrap(e); + } + } + + public DirectBytes acquire(long offset, long size) throws IOException { + MappedArenaStore mas = acquireMAS(offset, size); + return mas.bytes(offset - mas.offset, size); + } + + private MappedArenaStore acquireMAS(long offset, long size) throws IOException { + long end = offset + size; + for (MappedArenaStore store : storeList) { + if (store.offset >= offset && store.end <= end) + return store; + } + try { + MappedArenaStore store = new MappedArenaStore(offset, size); + storeList.add(store); + return store; + } catch (Exception e) { + throw wrap(e); + } + } + + @Override + public void close() throws IOException { + fileChannel.close(); + for (MappedArenaStore store : storeList) { + store.free(); + } + } + + class MappedArenaStore implements BytesStore { + private final long address; + private final Cleaner cleaner; + private final AtomicInteger refCount = new AtomicInteger(1); + final long offset, size, end; + + MappedArenaStore(long offset, long size) throws NoSuchMethodException, IllegalAccessException, InvocationTargetException, IOException { + this.offset = offset; + this.size = size; + this.end = offset + size; + if (randomAccessFile.length() < end) { + if (mode != FileChannel.MapMode.READ_WRITE) { + throw new IOException("Cannot resize file to " + end + " as mode is not READ_WRITE"); + } + + randomAccessFile.setLength(end); + } + this.address = map0(fileChannel, imodeFor(mode), offset, size); + this.cleaner = Cleaner.create(this, new Unmapper(address, size, fileChannel)); + } + + @Override + public ObjectSerializer objectSerializer() { + return objectSerializer; + } + + @Override + public long address() { + return address; + } + + @Override + public long size() { + return size; + } + + @Override + public void free() { + cleaner.clean(); + } + + @NotNull + public DirectBytes bytes() { + return new DirectBytes(this, refCount); + } + + @NotNull + public DirectBytes bytes(long offset, long length) { + return new DirectBytes(this, refCount, offset, length); + } + + @Override + public File file() { + return file; + } + } + + private static long map0(FileChannel fileChannel, int imode, long start, long size) throws NoSuchMethodException, IllegalAccessException, InvocationTargetException { + Method map0 = fileChannel.getClass().getDeclaredMethod("map0", int.class, long.class, long.class); + map0.setAccessible(true); + return (Long) map0.invoke(fileChannel, imode, start, size); + } + + private static void unmap0(long address, long size) throws IOException { + try { + Method unmap0 = FileChannelImpl.class.getDeclaredMethod("unmap0", long.class, long.class); + unmap0.setAccessible(true); + unmap0.invoke(null, address, size); + } catch (Exception e) { + throw wrap(e); + } + } + + private static IOException wrap(Throwable e) { + if (e instanceof InvocationTargetException) + e = e.getCause(); + if (e instanceof IOException) + return (IOException) e; + return new IOException(e); + } + + private static String accessModeFor(FileChannel.MapMode mode) { + return mode == FileChannel.MapMode.READ_WRITE ? "rw" : "r"; + } + + private static int imodeFor(FileChannel.MapMode mode) { + int imode = -1; + if (mode == FileChannel.MapMode.READ_ONLY) + imode = MAP_RO; + else if (mode == FileChannel.MapMode.READ_WRITE) + imode = MAP_RW; + else if (mode == FileChannel.MapMode.PRIVATE) + imode = MAP_PV; + assert (imode >= 0); + return imode; + } + + static class Unmapper implements Runnable { + private final long size; + private final FileChannel channel; + private volatile long address; + + Unmapper(long address, long size, FileChannel channel) { + assert (address != 0); + this.address = address; + this.size = size; + this.channel = channel; + } + + public void run() { + if (address == 0) + return; + + try { + unmap0(address, size); + address = 0; + + if (channel.isOpen()) { + channel.close(); + } + } catch (IOException e) { + e.printStackTrace(); + } + } + } +} + diff --git a/lang-sandbox/src/main/java/net/openhft/lang/data-types.txt b/lang-sandbox/src/main/java/net/openhft/lang/data-types.txt new file mode 100755 index 0000000..c3a68cb --- /dev/null +++ b/lang-sandbox/src/main/java/net/openhft/lang/data-types.txt @@ -0,0 +1,19 @@ +sequence_push +sequence_pop +group_push +group_pop +mapping_start {data} +mapping_end +named_mapping {text} {data} +numbered_mapping {number} {data} +comment {data} +document_start {text-type} +document_end +date {date-type} x +type {text-type} +define_alias {text-ref} {data} +alias {text-ref} + +need to recognise +- nested sequences +- nested mappings diff --git a/lang-sandbox/src/main/java/net/openhft/lang/io/OffHeapLock.java b/lang-sandbox/src/main/java/net/openhft/lang/io/OffHeapLock.java new file mode 100755 index 0000000..c980619 --- /dev/null +++ b/lang-sandbox/src/main/java/net/openhft/lang/io/OffHeapLock.java @@ -0,0 +1,34 @@ +/* + * Copyright 2014 Higher Frequency Trading + * + * http://www.higherfrequencytrading.com + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package net.openhft.lang.io; + +import java.util.concurrent.TimeUnit; + +/** +* Created by peter.lawrey on 03/08/14. +*/ +public interface OffHeapLock { + boolean tryLock(); + + void busyLock(); + + boolean busyLock(long time, TimeUnit timeUnit); + + void unlock(); +} diff --git a/lang-sandbox/src/main/java/net/openhft/lang/io/OffHeapReadWriteLock.java b/lang-sandbox/src/main/java/net/openhft/lang/io/OffHeapReadWriteLock.java new file mode 100755 index 0000000..4324956 --- /dev/null +++ b/lang-sandbox/src/main/java/net/openhft/lang/io/OffHeapReadWriteLock.java @@ -0,0 +1,191 @@ +/* + * Copyright 2014 Higher Frequency Trading + * + * http://www.higherfrequencytrading.com + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package net.openhft.lang.io; + +import java.util.concurrent.TimeUnit; + +/** + * Created by peter.lawrey on 03/08/14. + */ +public class OffHeapReadWriteLock { + private final Bytes bytes; + private final long offset; + private final ReadLock readLock; + private final WriteLock writeLock; + + public OffHeapReadWriteLock(Bytes bytes, long offset) { + this.bytes = bytes; + this.offset = offset; + this.readLock = new ReadLock(); + this.writeLock = new WriteLock(); + } + + long getLock() { + return bytes.readVolatileLong(offset); + } + + boolean trySetLock(long lock0, long lock2) { + return bytes.compareAndSwapLong(offset, lock0, lock2); + } + + static final String[] RW_MODES = {"none", "read", "write"}; + + public ReadLock readLock() { + return readLock; + } + + public WriteLock writeLock() { + return writeLock; + } + class ReadLock implements OffHeapLock { + @Override + public boolean tryLock() { + long lock = getLock(); + int rwMode = (int) (lock >>> 56) & 0x3; + if (rwMode < 2) { + long lock2 = (lock | (1L << 56)) + (1 << 16); + return trySetLock(lock, lock2); + } + return false; + } + + @Override + public void busyLock() { + busyLock(Long.MAX_VALUE, TimeUnit.NANOSECONDS); + } + + @Override + public boolean busyLock(long time, TimeUnit timeUnit) { + if (tryLock()) return true; + addWaitingReader(+1); + try { + long end = System.nanoTime() + timeUnit.convert(time, TimeUnit.NANOSECONDS); + do { + if (tryLock()) return true; + Thread.yield(); + } while (System.nanoTime() < end); + } finally { + addWaitingReader(-1); + } + return false; + } + + void addWaitingReader(int add) { + for(;;) { + long lock = getLock(); + int readerWaiting = (int) lock & 0xFFFF; + int readerWaiting2 = readerWaiting + add; + if (readerWaiting2 >= 1 << 16 || readerWaiting2 < 0) + // todo error state + return; + if (trySetLock(lock, (lock & ~0xFFFF) + readerWaiting2)) + return; + } + } + + @Override + public void unlock() { + for (; ; ) { + long lock = getLock(); + long lock2 = lock - (1 << 16); + if (trySetLock(lock, nextMode(lock2))) + return; + } + } + } + + public String toString() { + long lock = getLock(); + int rwMode = (int) (lock >>> 56) & 0x3; + int writerCount = (int) (lock >>> 48) & 0xFF; + int writerWaiting = (int) (lock >>> 32) & 0xFFFF; + int readerCount = (int) (lock >>> 16) & 0xFFFF; + int readerWaiting = (int) lock & 0xFFFF; + return RW_MODES[rwMode] + " w:" + writerCount + "/" + writerWaiting + " r:" + readerCount + "/" + readerWaiting; + } + + private long nextMode(long lock) { + int rwMode = (int) (lock >>> 56) & 0x3; +// int writerCount = (int) (lock >>> 48) & 0xFF; + int writerWaiting = (int) (lock >>> 32) & 0xFFFF; + int readerCount = (int) (lock >>> 16) & 0xFFFF; +// int readerWaiting = (int) lock & 0xFFFF; + if ((rwMode == 1 && readerCount == 0) || (rwMode == 2 && writerWaiting == 0)) + return lock & (~0L >>> 8); // clear mode. + return lock; + } + + class WriteLock implements OffHeapLock{ + @Override + public boolean tryLock() { + long lock = getLock(); + int rwMode = (int) (lock >>> 56) & 0x3; + int writerCount = (int) (lock >>> 48) & 0xFF; + if ((rwMode & 1) == 0 && ( writerCount == 0)) { + long lock2 = (lock | (2L << 56)) + (1L << 48); + return trySetLock(lock, lock2); + } + return false; + } + + @Override + public void busyLock() { + busyLock(Long.MAX_VALUE, TimeUnit.NANOSECONDS); + } + + @Override + public boolean busyLock(long time, TimeUnit timeUnit) { + if (tryLock()) return true; + addWaitingWriter(+1); + try { + long end = System.nanoTime() + timeUnit.convert(time, TimeUnit.NANOSECONDS); + do { + if (tryLock()) return true; + Thread.yield(); + } while (System.nanoTime() < end); + } finally { + addWaitingWriter(-1); + } + return false; + } + + void addWaitingWriter(int add) { + for(;;) { + long lock = getLock(); + int writerWaiting = (int) (lock >>> 32) & 0xFFFF; + int writerWaiting2 = writerWaiting + add; + if (writerWaiting2 >= 1 << 16 || writerWaiting2 < 0) + // todo error state + return; + if (trySetLock(lock, (lock & ~0xFFFF00000000L) + ((long) writerWaiting2 << 32))) + return; + } + } + + @Override + public void unlock() { + for (; ; ) { + long lock = getLock(); + long lock2 = lock - (1L << 48); + if (trySetLock(lock, nextMode(lock2))) + return; + } + } + } +} diff --git a/lang/src/main/java/net/openhft/lang/io/serialization/RawCopier.java b/lang-sandbox/src/main/java/net/openhft/lang/io/serialization/RawCopier.java index eaf5760..b5e224e 100644 --- a/lang/src/main/java/net/openhft/lang/io/serialization/RawCopier.java +++ b/lang-sandbox/src/main/java/net/openhft/lang/io/serialization/RawCopier.java @@ -1,11 +1,13 @@ /* - * Copyright 2013 Peter Lawrey + * Copyright 2014 Higher Frequency Trading + * + * http://www.higherfrequencytrading.com * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * - * http://www.apache.org/licenses/LICENSE-2.0 + * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, @@ -32,10 +34,11 @@ import static net.openhft.lang.io.NativeBytes.UNSAFE; * User: peter.lawrey Date: 22/09/13 Time: 16:51 */ public class RawCopier<T> { - final int start, end; + final int start; + final int end; private final Class<T> tClass; - public RawCopier(Class<T> tClass) { + private RawCopier(Class<T> tClass) { this.tClass = tClass; List<Field> fields = new ArrayList<Field>(); addAllFields(fields, tClass); @@ -93,12 +96,14 @@ public class RawCopier<T> { } } - private void addAllFields(List<Field> fields, Class tClass) { + private static void addAllFields(List<Field> fields, Class tClass) { if (tClass != null && tClass != Object.class) addAllFields(fields, tClass.getSuperclass()); - for (Field field : tClass.getDeclaredFields()) { - if (!Modifier.isStatic(field.getModifiers())) - fields.add(field); + if (tClass != null) { + for (Field field : tClass.getDeclaredFields()) { + if (!Modifier.isStatic(field.getModifiers())) + fields.add(field); + } } } } diff --git a/lang-sandbox/src/main/java/net/openhft/lang/io/serialization/direct/DirectSerializationFilter.java b/lang-sandbox/src/main/java/net/openhft/lang/io/serialization/direct/DirectSerializationFilter.java new file mode 100644 index 0000000..4513448 --- /dev/null +++ b/lang-sandbox/src/main/java/net/openhft/lang/io/serialization/direct/DirectSerializationFilter.java @@ -0,0 +1,50 @@ +/* + * Copyright 2014 Higher Frequency Trading + * + * http://www.higherfrequencytrading.com + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package net.openhft.lang.io.serialization.direct; + +import java.lang.reflect.Field; +import java.util.ArrayList; +import java.util.Collections; +import java.util.List; + +import static net.openhft.lang.io.serialization.direct.FieldMetadata.*; + +final class DirectSerializationFilter { + public static List<Field> stopAtFirstIneligibleField(List<Field> fields) { + ArrayList<Field> eligibleFields = new ArrayList<Field>(); + for (Field f : fields) { + if (checkEligible(f)) { + eligibleFields.add(f); + + } else { + break; + } + } + + return eligibleFields.isEmpty() ? + Collections.<Field>emptyList() : + eligibleFields; + } + + private static boolean checkEligible(Field f) { + return isPrimitive(f) && + !isStatic(f) && + !isTransient(f); + } +}
\ No newline at end of file diff --git a/lang/src/main/java/net/openhft/lang/io/serialization/direct/DirectSerializationMetadata.java b/lang-sandbox/src/main/java/net/openhft/lang/io/serialization/direct/DirectSerializationMetadata.java index 2fcd50c..b18e521 100644 --- a/lang/src/main/java/net/openhft/lang/io/serialization/direct/DirectSerializationMetadata.java +++ b/lang-sandbox/src/main/java/net/openhft/lang/io/serialization/direct/DirectSerializationMetadata.java @@ -1,3 +1,21 @@ +/* + * Copyright 2014 Higher Frequency Trading + * + * http://www.higherfrequencytrading.com + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + package net.openhft.lang.io.serialization.direct; import net.openhft.lang.Jvm; @@ -12,10 +30,10 @@ public class DirectSerializationMetadata { private static final int OBJECT_ALIGNMENT_MASK = OBJECT_ALIGNMENT - 1; static final long NATIVE_WORD_SIZE = Jvm.is64Bit() ? 8 : 4; - static final long OOP_SIZE = UNSAFE.arrayIndexScale(Object[].class); + private static final long OOP_SIZE = UNSAFE.arrayIndexScale(Object[].class); static final long OBJECT_HEADER_SIZE = NATIVE_WORD_SIZE + OOP_SIZE; // Object header has a native sized mark word + variable sized oop to klass meta object - public static final SerializationMetadata EmptyObjectMetadata = new SerializationMetadata(0, 0); + private static final SerializationMetadata EmptyObjectMetadata = new SerializationMetadata(0, 0); public static final class SerializationMetadata { final long start; @@ -58,7 +76,7 @@ public class DirectSerializationMetadata { return new Offsets(minOffset, maxOffset); } - static long padToObjectAlignment(long length) { + private static long padToObjectAlignment(long length) { if ((length & OBJECT_ALIGNMENT_MASK) != 0) { long padding = OBJECT_ALIGNMENT - (length & OBJECT_ALIGNMENT_MASK); length += padding; diff --git a/lang-sandbox/src/main/java/net/openhft/lang/io/serialization/direct/FieldMetadata.java b/lang-sandbox/src/main/java/net/openhft/lang/io/serialization/direct/FieldMetadata.java new file mode 100644 index 0000000..00f96c5 --- /dev/null +++ b/lang-sandbox/src/main/java/net/openhft/lang/io/serialization/direct/FieldMetadata.java @@ -0,0 +1,41 @@ +/* + * Copyright 2014 Higher Frequency Trading + * + * http://www.higherfrequencytrading.com + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package net.openhft.lang.io.serialization.direct; + +import java.lang.reflect.Field; +import java.lang.reflect.Modifier; + +final class FieldMetadata { + public static boolean isPrimitive(Field f) { + return f.getType().isPrimitive(); + } + + public static boolean isPrimitiveArray(Field f) { + Class<?> clazz = f.getType(); + return clazz.isArray() && clazz.getComponentType().isPrimitive(); + } + + public static boolean isStatic(Field f) { + return Modifier.isStatic(f.getModifiers()); + } + + public static boolean isTransient(Field f) { + return Modifier.isTransient(f.getModifiers()); + } +} diff --git a/lang/src/main/java/net/openhft/lang/io/serialization/direct/Introspect.java b/lang-sandbox/src/main/java/net/openhft/lang/io/serialization/direct/Introspect.java index 28ff782..5152571 100644 --- a/lang/src/main/java/net/openhft/lang/io/serialization/direct/Introspect.java +++ b/lang-sandbox/src/main/java/net/openhft/lang/io/serialization/direct/Introspect.java @@ -1,11 +1,13 @@ /* - * Copyright 2013 Peter Lawrey + * Copyright 2014 Higher Frequency Trading + * + * http://www.higherfrequencytrading.com * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * - * http://www.apache.org/licenses/LICENSE-2.0 + * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, diff --git a/lang/src/main/java/net/openhft/lang/io/serialization/direct/ObjectMarshaller.java b/lang-sandbox/src/main/java/net/openhft/lang/io/serialization/direct/ObjectMarshaller.java index 24f8a48..2520634 100644 --- a/lang/src/main/java/net/openhft/lang/io/serialization/direct/ObjectMarshaller.java +++ b/lang-sandbox/src/main/java/net/openhft/lang/io/serialization/direct/ObjectMarshaller.java @@ -1,3 +1,21 @@ +/* + * Copyright 2014 Higher Frequency Trading + * + * http://www.higherfrequencytrading.com + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + package net.openhft.lang.io.serialization.direct; import net.openhft.lang.io.Bytes; diff --git a/lang/src/main/java/net/openhft/lang/io/serialization/direct/ObjectMarshallers.java b/lang-sandbox/src/main/java/net/openhft/lang/io/serialization/direct/ObjectMarshallers.java index bb9e35d..d0f0a03 100644 --- a/lang/src/main/java/net/openhft/lang/io/serialization/direct/ObjectMarshallers.java +++ b/lang-sandbox/src/main/java/net/openhft/lang/io/serialization/direct/ObjectMarshallers.java @@ -1,16 +1,36 @@ +/* + * Copyright 2014 Higher Frequency Trading + * + * http://www.higherfrequencytrading.com + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + package net.openhft.lang.io.serialization.direct; -import net.openhft.lang.io.NativeBytes; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; import java.lang.reflect.Field; -import java.util.*; -import java.util.logging.Logger; +import java.util.Collection; +import java.util.HashMap; +import java.util.List; +import java.util.Map; -import static java.util.logging.Level.WARNING; import static net.openhft.lang.io.serialization.direct.DirectSerializationMetadata.SerializationMetadata; -public final class ObjectMarshallers { - private static final Logger Log = Logger.getLogger(ObjectMarshallers.class.getName()); +final class ObjectMarshallers { + private static final Logger Log = LoggerFactory.getLogger(ObjectMarshallers.class); private static final Map<Class, ObjectMarshaller> metadata = new HashMap<Class, ObjectMarshaller>(); @@ -26,12 +46,13 @@ public final class ObjectMarshallers { if (hasIneligibleFields(fields, eligibleFields)) { WarnAboutIneligibleFields.apply(clazz, fields, eligibleFields); serializationMetadata = DirectSerializationMetadata.extractMetadataForPartialCopy(eligibleFields); + } else { serializationMetadata = DirectSerializationMetadata.extractMetadata(eligibleFields); } om = new ObjectMarshaller<T>(serializationMetadata); - Log.log(WARNING, String.format("Class %s has metadata %s", clazz.getName(), serializationMetadata)); + Log.warn("Class {} has metadata {}", clazz.getName(), serializationMetadata); metadata.put(clazz, om); } @@ -45,11 +66,11 @@ public final class ObjectMarshallers { private static class WarnAboutIneligibleFields { static void apply(Class clazz, List<Field> allFields, List<Field> eligibleFields) { List<Field> ineligibleFields = allFields.subList(eligibleFields.size(), allFields.size()); - Log.log(WARNING, String.format( - "The following fields in Class %s will not be copied by ObjectMarshaller:\n%s", - clazz.getName(), - commaSeparate(ineligibleFields) - )); + Log.warn( + "The following fields in Class {} will not be copied by ObjectMarshaller:\n{}", + clazz.getName(), + commaSeparate(ineligibleFields) + ); } private static String commaSeparate(Collection<Field> fields) { @@ -57,9 +78,10 @@ public final class ObjectMarshallers { boolean first = true; for (Field field : fields) { if (first) { - sb.append("\t"); + sb.append('\t'); sb.append(field.getName()); first = false; + } else { sb.append("\n\t"); sb.append(field.getName()); diff --git a/lang/src/main/java/net/openhft/lang/model/ClassModel.java b/lang-sandbox/src/main/java/net/openhft/lang/model/ClassModel.java index 40bc614..2777560 100644 --- a/lang/src/main/java/net/openhft/lang/model/ClassModel.java +++ b/lang-sandbox/src/main/java/net/openhft/lang/model/ClassModel.java @@ -1,11 +1,13 @@ /* - * Copyright 2013 Peter Lawrey + * Copyright 2014 Higher Frequency Trading + * + * http://www.higherfrequencytrading.com * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * - * http://www.apache.org/licenses/LICENSE-2.0 + * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, diff --git a/lang/src/main/java/net/openhft/lang/model/DataValueMetaModel.java b/lang-sandbox/src/main/java/net/openhft/lang/model/DataValueMetaModel.java index 75263f5..a381ad0 100644 --- a/lang/src/main/java/net/openhft/lang/model/DataValueMetaModel.java +++ b/lang-sandbox/src/main/java/net/openhft/lang/model/DataValueMetaModel.java @@ -1,11 +1,13 @@ /* - * Copyright 2013 Peter Lawrey + * Copyright 2014 Higher Frequency Trading + * + * http://www.higherfrequencytrading.com * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * - * http://www.apache.org/licenses/LICENSE-2.0 + * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, @@ -24,7 +26,7 @@ import java.util.HashSet; import java.util.List; import java.util.Set; -public class DataValueMetaModel { +class DataValueMetaModel { private final Set<Class> ignoredClasses = new HashSet<Class>(); private final List<MethodFilter> filters = new ArrayList<MethodFilter>(); @@ -40,11 +42,11 @@ public class DataValueMetaModel { } } - public void addIgnoredClass(Class aClass) { + void addIgnoredClass(Class aClass) { ignoredClasses.add(aClass); } - public void addMethodFilter(MethodFilter filter) { + void addMethodFilter(MethodFilter filter) { int pos = insertionPoint(filter); filters.add(pos, filter); } @@ -55,6 +57,4 @@ public class DataValueMetaModel { return i; return filters.size(); } - - } diff --git a/lang/src/main/java/net/openhft/lang/model/MethodFilter.java b/lang-sandbox/src/main/java/net/openhft/lang/model/MethodFilter.java index 32a3908..22283ba 100644 --- a/lang/src/main/java/net/openhft/lang/model/MethodFilter.java +++ b/lang-sandbox/src/main/java/net/openhft/lang/model/MethodFilter.java @@ -1,11 +1,13 @@ /* - * Copyright 2013 Peter Lawrey + * Copyright 2014 Higher Frequency Trading + * + * http://www.higherfrequencytrading.com * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * - * http://www.apache.org/licenses/LICENSE-2.0 + * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, @@ -18,7 +20,7 @@ package net.openhft.lang.model; import java.lang.reflect.Method; -public interface MethodFilter { +interface MethodFilter { int matches(); String nameFor(Method method, Class<?>[] parameterTypes); diff --git a/lang/src/main/java/net/openhft/lang/model/MethodTemplate.java b/lang-sandbox/src/main/java/net/openhft/lang/model/MethodTemplate.java index 1a38eee..f41e502 100644 --- a/lang/src/main/java/net/openhft/lang/model/MethodTemplate.java +++ b/lang-sandbox/src/main/java/net/openhft/lang/model/MethodTemplate.java @@ -1,11 +1,13 @@ /* - * Copyright 2013 Peter Lawrey + * Copyright 2014 Higher Frequency Trading + * + * http://www.higherfrequencytrading.com * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * - * http://www.apache.org/licenses/LICENSE-2.0 + * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, diff --git a/lang/src/main/java/net/openhft/lang/model/VanillaFilter.java b/lang-sandbox/src/main/java/net/openhft/lang/model/VanillaFilter.java index 3ec00bc..b825675 100644 --- a/lang/src/main/java/net/openhft/lang/model/VanillaFilter.java +++ b/lang-sandbox/src/main/java/net/openhft/lang/model/VanillaFilter.java @@ -1,11 +1,13 @@ /* - * Copyright 2013 Peter Lawrey + * Copyright 2014 Higher Frequency Trading + * + * http://www.higherfrequencytrading.com * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * - * http://www.apache.org/licenses/LICENSE-2.0 + * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, diff --git a/lang-sandbox/src/test/java/net/openhft/lang/io/OffHeapReadWriteLockTest.java b/lang-sandbox/src/test/java/net/openhft/lang/io/OffHeapReadWriteLockTest.java new file mode 100644 index 0000000..7270a09 --- /dev/null +++ b/lang-sandbox/src/test/java/net/openhft/lang/io/OffHeapReadWriteLockTest.java @@ -0,0 +1,77 @@ +/* + * Copyright 2014 Higher Frequency Trading + * + * http://www.higherfrequencytrading.com + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package net.openhft.lang.io; + +import net.openhft.lang.io.DirectStore; +import net.openhft.lang.io.OffHeapReadWriteLock; +import org.junit.Test; + +import static org.junit.Assert.*; + +public class OffHeapReadWriteLockTest { + @Test + public void readCycle() { + OffHeapReadWriteLock ohrwl = new OffHeapReadWriteLock(DirectStore.allocate(64).bytes(), 0); + assertEquals("none w:0/0 r:0/0", ohrwl.toString()); + assertTrue(ohrwl.readLock().tryLock()); + assertEquals("read w:0/0 r:1/0", ohrwl.toString()); + assertTrue(ohrwl.readLock().tryLock()); + assertEquals("read w:0/0 r:2/0", ohrwl.toString()); + ohrwl.readLock().unlock(); + assertEquals("read w:0/0 r:1/0", ohrwl.toString()); + ohrwl.readLock().unlock(); + assertEquals("none w:0/0 r:0/0", ohrwl.toString()); + } + + @Test + public void writeCycle() { + OffHeapReadWriteLock ohrwl = new OffHeapReadWriteLock(DirectStore.allocate(64).bytes(), 0); + assertEquals("none w:0/0 r:0/0", ohrwl.toString()); + assertTrue(ohrwl.writeLock().tryLock()); + assertEquals("write w:1/0 r:0/0", ohrwl.toString()); + assertFalse(ohrwl.writeLock().tryLock()); + ohrwl.writeLock().addWaitingWriter(1); + assertEquals("write w:1/1 r:0/0", ohrwl.toString()); + ohrwl.writeLock().unlock(); + assertEquals("write w:0/1 r:0/0", ohrwl.toString()); + assertTrue(ohrwl.writeLock().tryLock()); + ohrwl.writeLock().addWaitingWriter(-1); + assertEquals("write w:1/0 r:0/0", ohrwl.toString()); + ohrwl.writeLock().unlock(); + assertEquals("none w:0/0 r:0/0", ohrwl.toString()); + } + + @Test + public void waitingCycle() { + OffHeapReadWriteLock ohrwl = new OffHeapReadWriteLock(DirectStore.allocate(64).bytes(), 0); + assertEquals("none w:0/0 r:0/0", ohrwl.toString()); + assertTrue(ohrwl.writeLock().tryLock()); + assertEquals("write w:1/0 r:0/0", ohrwl.toString()); + assertFalse(ohrwl.readLock().tryLock()); + ohrwl.readLock().addWaitingReader(2); + assertEquals("write w:1/0 r:0/2", ohrwl.toString()); + ohrwl.writeLock().unlock(); + assertEquals("none w:0/0 r:0/2", ohrwl.toString()); + assertTrue(ohrwl.readLock().tryLock()); + ohrwl.readLock().addWaitingReader(-1); + assertEquals("read w:0/0 r:1/1", ohrwl.toString()); + assertTrue(ohrwl.readLock().tryLock()); + ohrwl.readLock().addWaitingReader(-1); + } +}
\ No newline at end of file diff --git a/lang/src/test/java/net/openhft/lang/io/serialization/RawCopierTest.java b/lang-sandbox/src/test/java/net/openhft/lang/io/serialization/RawCopierTest.java index 1b89806..53d7abf 100644 --- a/lang/src/test/java/net/openhft/lang/io/serialization/RawCopierTest.java +++ b/lang-sandbox/src/test/java/net/openhft/lang/io/serialization/RawCopierTest.java @@ -1,11 +1,13 @@ /* - * Copyright 2013 Peter Lawrey + * Copyright 2014 Higher Frequency Trading + * + * http://www.higherfrequencytrading.com * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * - * http://www.apache.org/licenses/LICENSE-2.0 + * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, @@ -52,8 +54,8 @@ public class RawCopierTest { @Test public void testReadWrite() { - DirectStore ds = new DirectStore(null, 1024); - DirectBytes db = ds.createSlice(); + DirectStore ds = DirectStore.allocate(1024); + DirectBytes db = ds.bytes(); RawCopier<A> aRawCopier = RawCopier.copies(A.class); A a = new A(); a.i = 111; diff --git a/lang/src/test/java/net/openhft/lang/io/serialization/direct/DirectSerializationFilterTest.java b/lang-sandbox/src/test/java/net/openhft/lang/io/serialization/direct/DirectSerializationFilterTest.java index 35b5c6e..16f6b8b 100644 --- a/lang/src/test/java/net/openhft/lang/io/serialization/direct/DirectSerializationFilterTest.java +++ b/lang-sandbox/src/test/java/net/openhft/lang/io/serialization/direct/DirectSerializationFilterTest.java @@ -1,12 +1,32 @@ +/* + * Copyright 2014 Higher Frequency Trading + * + * http://www.higherfrequencytrading.com + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + package net.openhft.lang.io.serialization.direct; import org.junit.Test; import java.lang.reflect.Field; -import java.util.*; +import java.util.ArrayList; +import java.util.List; import static net.openhft.lang.io.serialization.direct.DirectSerializationFilter.stopAtFirstIneligibleField; -import static org.junit.Assert.*; +import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertTrue; public class DirectSerializationFilterTest { diff --git a/lang/src/test/java/net/openhft/lang/io/serialization/direct/DirectSerializationMetadataTest.java b/lang-sandbox/src/test/java/net/openhft/lang/io/serialization/direct/DirectSerializationMetadataTest.java index 1c02082..4642107 100644 --- a/lang/src/test/java/net/openhft/lang/io/serialization/direct/DirectSerializationMetadataTest.java +++ b/lang-sandbox/src/test/java/net/openhft/lang/io/serialization/direct/DirectSerializationMetadataTest.java @@ -1,3 +1,21 @@ +/* + * Copyright 2014 Higher Frequency Trading + * + * http://www.higherfrequencytrading.com + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + package net.openhft.lang.io.serialization.direct; import org.junit.Test; diff --git a/lang/src/test/java/net/openhft/lang/io/serialization/direct/IntrospectTest.java b/lang-sandbox/src/test/java/net/openhft/lang/io/serialization/direct/IntrospectTest.java index 1c5879b..609d020 100644 --- a/lang/src/test/java/net/openhft/lang/io/serialization/direct/IntrospectTest.java +++ b/lang-sandbox/src/test/java/net/openhft/lang/io/serialization/direct/IntrospectTest.java @@ -1,9 +1,28 @@ +/* + * Copyright 2014 Higher Frequency Trading + * + * http://www.higherfrequencytrading.com + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + package net.openhft.lang.io.serialization.direct; import org.junit.Test; import java.lang.reflect.Field; -import java.util.*; +import java.util.ArrayList; +import java.util.List; import static net.openhft.lang.io.serialization.direct.TestClasses.*; import static org.junit.Assert.assertEquals; diff --git a/lang/src/test/java/net/openhft/lang/io/serialization/direct/ObjectMarshallerTest.java b/lang-sandbox/src/test/java/net/openhft/lang/io/serialization/direct/ObjectMarshallerTest.java index 7226a17..7c7efc9 100644 --- a/lang/src/test/java/net/openhft/lang/io/serialization/direct/ObjectMarshallerTest.java +++ b/lang-sandbox/src/test/java/net/openhft/lang/io/serialization/direct/ObjectMarshallerTest.java @@ -1,6 +1,25 @@ +/* + * Copyright 2014 Higher Frequency Trading + * + * http://www.higherfrequencytrading.com + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + package net.openhft.lang.io.serialization.direct; -import net.openhft.lang.io.*; +import net.openhft.lang.io.ByteBufferBytes; +import net.openhft.lang.io.Bytes; import org.junit.Test; import java.nio.ByteBuffer; @@ -8,7 +27,6 @@ import java.util.Collections; import static net.openhft.lang.io.serialization.direct.TestClasses.*; import static org.junit.Assert.*; -import static org.junit.Assert.assertEquals; public class ObjectMarshallerTest { @@ -22,12 +40,12 @@ public class ObjectMarshallerTest { marshaller.write(b, p); p.a = 0; - b.reset(); + b.clear(); marshaller.read(b, p); assertEquals(55, p.a); - b.reset(); + b.clear(); Primitives1 newP = new Primitives1(); marshaller.read(b, newP); @@ -47,13 +65,13 @@ public class ObjectMarshallerTest { p.a = 0; p.b = 0; - b.reset(); + b.clear(); marshaller.read(b, p); assertEquals(55, p.a); assertEquals(-10, p.b); - b.reset(); + b.clear(); Primitives2 newP = new Primitives2(); marshaller.read(b, newP); @@ -76,14 +94,14 @@ public class ObjectMarshallerTest { p.a = 0; p.b = 0; p.c = 0; - b.reset(); + b.clear(); marshaller.read(b, p); assertEquals(55, p.a); assertEquals(-10, p.b); assertEquals(92, p.c); - b.reset(); + b.clear(); Primitives3 newP = new Primitives3(); marshaller.read(b, newP); @@ -103,12 +121,12 @@ public class ObjectMarshallerTest { marshaller.write(b, p); p.a = false; - b.reset(); + b.clear(); marshaller.read(b, p); assertTrue(p.a); - b.reset(); + b.clear(); Primitives4 newP = new Primitives4(); marshaller.read(b, newP); @@ -128,13 +146,13 @@ public class ObjectMarshallerTest { p.a = false; p.b = 0; - b.reset(); + b.clear(); marshaller.read(b, p); assertTrue(p.a); assertEquals(Long.MIN_VALUE, p.b); - b.reset(); + b.clear(); Primitives5 newP = new Primitives5(); marshaller.read(b, newP); @@ -161,7 +179,7 @@ public class ObjectMarshallerTest { p.c = 0; p.d = 0; p.e = 0; - b.reset(); + b.clear(); marshaller.read(b, p); assertTrue(p.a); @@ -170,7 +188,7 @@ public class ObjectMarshallerTest { assertEquals(Long.MAX_VALUE, p.d); assertEquals(Double.MAX_VALUE, p.e, 0); - b.reset(); + b.clear(); Primitives6 newP = new Primitives6(); marshaller.read(b, newP); @@ -210,7 +228,7 @@ public class ObjectMarshallerTest { m.transientShort = 0; m.transientObject = null; - b.reset(); + b.clear(); marshaller.read(b, m); assertEquals(Integer.MIN_VALUE + 1, m.intField); @@ -223,7 +241,7 @@ public class ObjectMarshallerTest { assertNull(m.transientObject); assertEquals(0, m.transientShort); - b.reset(); + b.clear(); MixedFields newM = new MixedFields(); marshaller.read(b, newM); @@ -239,7 +257,7 @@ public class ObjectMarshallerTest { assertEquals(0, newM.transientShort); } - private ByteBufferBytes createByteStore() { - return new ByteBufferBytes(ByteBuffer.allocate(64)); + private Bytes createByteStore() { + return ByteBufferBytes.wrap(ByteBuffer.allocate(64)); } }
\ No newline at end of file diff --git a/lang/src/test/java/net/openhft/lang/io/serialization/direct/TestClasses.java b/lang-sandbox/src/test/java/net/openhft/lang/io/serialization/direct/TestClasses.java index 7b173dd..420c2f2 100644 --- a/lang/src/test/java/net/openhft/lang/io/serialization/direct/TestClasses.java +++ b/lang-sandbox/src/test/java/net/openhft/lang/io/serialization/direct/TestClasses.java @@ -1,6 +1,25 @@ +/* + * Copyright 2014 Higher Frequency Trading + * + * http://www.higherfrequencytrading.com + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + package net.openhft.lang.io.serialization.direct; -import java.util.*; +import java.util.Collections; +import java.util.List; class TestClasses { @@ -39,7 +58,7 @@ class TestClasses { } public static class MixedFields { - public static int staticIntField ; + public static int staticIntField; public static double[] staticDoubleArray; public static List<String> staticStringList = Collections.singletonList("S1"); diff --git a/lang-test/pom.xml b/lang-test/pom.xml new file mode 100755 index 0000000..ef93d8d --- /dev/null +++ b/lang-test/pom.xml @@ -0,0 +1,233 @@ +<?xml version="1.0" encoding="UTF-8"?> +<!-- + ~ Copyright (C) 2015 higherfrequencytrading.com + ~ + ~ This program is free software: you can redistribute it and/or modify + ~ it under the terms of the GNU Lesser General Public License as published by + ~ the Free Software Foundation, either version 3 of the License. + ~ + ~ 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 Lesser General Public License for more details. + ~ + ~ You should have received a copy of the GNU Lesser General Public License + ~ along with this program. If not, see <http://www.gnu.org/licenses/>. + --> + +<project xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" + xmlns="http://maven.apache.org/POM/4.0.0" + xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd"> + + <parent> + <groupId>net.openhft</groupId> + <artifactId>java-parent-pom</artifactId> + <version>1.1.4</version> + <relativePath/> + </parent> + + <modelVersion>4.0.0</modelVersion> + + <artifactId>lang-test</artifactId> + <version>6.7.4-SNAPSHOT</version> + <packaging>bundle</packaging> + + <name>OpenHFT/Java-Lang/lang-test</name> + <description>Java Lang library for High Frequency Trading (Java 6+)</description> + + <dependencyManagement> + <dependencies> + <dependency> + <groupId>net.openhft</groupId> + <artifactId>third-party-bom</artifactId> + <type>pom</type> + <version>3.4.20</version> + <scope>import</scope> + </dependency> + <dependency> + <groupId>net.openhft</groupId> + <artifactId>lang</artifactId> + <version>6.7.1-SNAPSHOT</version> + </dependency> + <dependency> + <groupId>net.openhft</groupId> + <artifactId>compiler</artifactId> + <version>2.2.0</version> + </dependency> + <dependency> + <groupId>net.openhft</groupId> + <artifactId>affinity</artifactId> + <version>3.0</version> + </dependency> + <dependency> + <groupId>net.openhft</groupId> + <artifactId>collections</artifactId> + <version>3.1.0</version> + </dependency> + </dependencies> + </dependencyManagement> + + <dependencies> + <dependency> + <groupId>com.sun.java</groupId> + <artifactId>tools</artifactId> + </dependency> + <dependency> + <groupId>net.openhft</groupId> + <artifactId>lang</artifactId> + </dependency> + <dependency> + <groupId>net.openhft</groupId> + <artifactId>affinity</artifactId> + </dependency> + <dependency> + <groupId>net.openhft</groupId> + <artifactId>compiler</artifactId> + </dependency> + <dependency> + <groupId>net.openhft</groupId> + <artifactId>collections</artifactId> + </dependency> + <dependency> + <groupId>javax.inject</groupId> + <artifactId>javax.inject</artifactId> + <scope>test</scope> + </dependency> + <dependency> + <groupId>org.osgi</groupId> + <artifactId>org.osgi.core</artifactId> + <scope>test</scope> + </dependency> + <dependency> + <groupId>org.osgi</groupId> + <artifactId>org.osgi.compendium</artifactId> + <scope>test</scope> + </dependency> + <dependency> + <groupId>org.ops4j.pax.exam</groupId> + <artifactId>pax-exam-container-native</artifactId> + <scope>test</scope> + </dependency> + + <dependency> + <groupId>org.ops4j.pax.exam</groupId> + <artifactId>pax-exam-junit4</artifactId> + <scope>test</scope> + </dependency> + <dependency> + <groupId>org.ops4j.pax.exam</groupId> + <artifactId>pax-exam-link-mvn</artifactId> + <scope>test</scope> + </dependency> + <dependency> + <groupId>org.ops4j.pax.url</groupId> + <artifactId>pax-url-aether</artifactId> + <scope>test</scope> + </dependency> + <dependency> + <groupId>org.ops4j.pax.url</groupId> + <artifactId>pax-url-reference</artifactId> + <scope>test</scope> + </dependency> + <dependency> + <groupId>org.apache.felix</groupId> + <artifactId>org.apache.felix.framework</artifactId> + <version>4.4.1</version> + <scope>test</scope> + </dependency> + <dependency> + <groupId>org.slf4j</groupId> + <artifactId>slf4j-api</artifactId> + <scope>test</scope> + </dependency> + <dependency> + <groupId>org.slf4j</groupId> + <artifactId>slf4j-simple</artifactId> + <scope>test</scope> + </dependency> + </dependencies> + + <build> + <plugins> + <plugin> + <groupId>org.apache.maven.plugins</groupId> + <artifactId>maven-compiler-plugin</artifactId> + <configuration> + <source>1.6</source> + <target>1.6</target> + <encoding>UTF-8</encoding> + </configuration> + </plugin> + <plugin> + <groupId>org.apache.maven.plugins</groupId> + <artifactId>maven-surefire-plugin</artifactId> + <configuration> + <systemPropertyVariables> + <main.basedir>${project.basedir}</main.basedir> + <project.build.directory>${project.build.directory}</project.build.directory> + + </systemPropertyVariables> + </configuration> + </plugin> + <!-- + generate maven dependencies versions file that can be used later + to install the right bundle in test phase. + + The file is: + + target/classes/META-INF/maven/dependencies.properties + --> + <plugin> + <groupId>org.apache.servicemix.tooling</groupId> + <artifactId>depends-maven-plugin</artifactId> + <executions> + <execution> + <id>generate-depends-file</id> + <goals> + <goal>generate-depends-file</goal> + </goals> + </execution> + </executions> + </plugin> + + <plugin> + <groupId>org.apache.felix</groupId> + <artifactId>maven-bundle-plugin</artifactId> + <extensions>true</extensions> + <configuration> + <instructions> + <Bundle-SymbolicName>${project.groupId}.${project.artifactId}</Bundle-SymbolicName> + <Bundle-Name>OpenHFT :: ${project.artifactId}</Bundle-Name> + <Bundle-Version>${project.version}</Bundle-Version> + <Export-Package> + net.openhft.langosgi, + net.openhft.langosgi.model + </Export-Package> + <Import-Package> + net.openhft.lang.io.serialization, + net.openhft.lang.model, + * + </Import-Package> + </instructions> + </configuration> + <executions> + <!-- + This execution makes sure that the manifest is available + when the tests are executed + --> + <execution> + <goals> + <goal>manifest</goal> + </goals> + </execution> + </executions> + </plugin> + </plugins> + </build> + + <scm> + <url>scm:git:https://github.com/OpenHFT/Java-Lang.git</url> + </scm> + +</project> + diff --git a/lang-integration/src/test/java/net/openhft/lang/io/LockingViaMMapWithThreadIdMain.java b/lang-test/src/main/java/net/openhft/lang/io/LockingViaMMapWithThreadIdMain.java index 23877c5..315fb5e 100644..100755 --- a/lang-integration/src/test/java/net/openhft/lang/io/LockingViaMMapWithThreadIdMain.java +++ b/lang-test/src/main/java/net/openhft/lang/io/LockingViaMMapWithThreadIdMain.java @@ -1,27 +1,26 @@ /* - * Copyright 2013 Peter Lawrey + * Copyright (C) 2015 higherfrequencytrading.com * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as published by + * the Free Software Foundation, either version 3 of the License. * - * http://www.apache.org/licenses/LICENSE-2.0 + * 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 Lesser General Public License for more details. * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. + * You should have received a copy of the GNU Lesser General Public License + * along with this program. If not, see <http://www.gnu.org/licenses/>. */ package net.openhft.lang.io; -import net.openhft.affinity.AffinitySupport; +import net.openhft.affinity.Affinity; import java.io.File; import java.io.IOException; import java.io.RandomAccessFile; -import java.nio.ByteOrder; import java.nio.MappedByteBuffer; import java.nio.channels.FileChannel; @@ -29,8 +28,8 @@ import java.nio.channels.FileChannel; * User: peter * Date: 22/12/13 * Time: 11:05 - * <p/> - * Toggled 10,000,128 times with an average delay of 20 ns on i7-4700 + * + * <p>Toggled 10,000,128 times with an average delay of 20 ns on i7-4700 * Toggled 10,000,128 times with an average delay of 14 ns on i7-3970X */ public class LockingViaMMapWithThreadIdMain { @@ -51,10 +50,8 @@ public class LockingViaMMapWithThreadIdMain { MappedByteBuffer mbb = fc.map(FileChannel.MapMode.READ_WRITE, 0, RECORDS * RECORD_SIZE); // set the the Thread.getId() to match the process thread id // this way the getId() can be used across processes.. - AffinitySupport.setThreadId(); - AffinitySupport.setAffinity(toggleTo ? 1 << 3 : 1 << 2); - ByteBufferBytes bytes = new ByteBufferBytes(mbb.order(ByteOrder.nativeOrder())); - bytes.setCurrentThread(); + Affinity.setAffinity(toggleTo ? 1 << 3 : 1 << 2); + Bytes bytes = ByteBufferBytes.wrap(mbb); long start = 0; for (int i = -WARMUP / RECORDS; i < (RUNS + RECORDS - 1) / RECORDS; i++) { @@ -69,6 +66,7 @@ public class LockingViaMMapWithThreadIdMain { if (t == 0) if (i >= 0) { throw new AssertionError("Didn't toggle in time !??"); + } else { Thread.sleep(200); } diff --git a/lang-osgi/src/main/java/net/openhft/langosgi/model/JavaBeanInterface.java b/lang-test/src/main/java/net/openhft/langosgi/model/JavaBeanInterface.java index 9675d65..3ecbd22 100644..100755 --- a/lang-osgi/src/main/java/net/openhft/langosgi/model/JavaBeanInterface.java +++ b/lang-test/src/main/java/net/openhft/langosgi/model/JavaBeanInterface.java @@ -1,17 +1,17 @@ /* - * Copyright 2013 Peter Lawrey + * Copyright (C) 2015 higherfrequencytrading.com * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as published by + * the Free Software Foundation, either version 3 of the License. * - * http://www.apache.org/licenses/LICENSE-2.0 + * 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 Lesser General Public License for more details. * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. + * You should have received a copy of the GNU Lesser General Public License + * along with this program. If not, see <http://www.gnu.org/licenses/>. */ package net.openhft.langosgi.model; diff --git a/lang-test/src/test/java/net/openhft/lang/osgi/OSGiBundleTest.java b/lang-test/src/test/java/net/openhft/lang/osgi/OSGiBundleTest.java new file mode 100644 index 0000000..23873fb --- /dev/null +++ b/lang-test/src/test/java/net/openhft/lang/osgi/OSGiBundleTest.java @@ -0,0 +1,85 @@ +/* + * Copyright (C) 2015 higherfrequencytrading.com + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as published by + * the Free Software Foundation, either version 3 of the License. + * + * 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 Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with this program. If not, see <http://www.gnu.org/licenses/>. + */ + +package net.openhft.lang.osgi; + +import org.junit.Ignore; +import org.junit.Test; +import org.junit.runner.RunWith; +import org.ops4j.pax.exam.Configuration; +import org.ops4j.pax.exam.Option; +import org.ops4j.pax.exam.junit.PaxExam; +import org.osgi.framework.Bundle; +import org.osgi.framework.BundleContext; + +import javax.inject.Inject; + +import static org.junit.Assert.*; +import static org.ops4j.pax.exam.CoreOptions.*; + +/** + * @author lburgazzoli + */ +@Ignore +@RunWith(PaxExam.class) +public class OSGiBundleTest extends OSGiTestBase { + @Inject + BundleContext context; + + @Configuration + public Option[] config() { + return options( + systemProperty("org.osgi.framework.storage.clean").value("true"), + systemProperty("org.ops4j.pax.logging.DefaultServiceLog.level").value("WARN"), + mavenBundleAsInProject("org.slf4j","slf4j-api"), + mavenBundleAsInProject("org.slf4j","slf4j-simple").noStart(), + mavenBundleAsInProject("net.openhft","affinity"), + mavenBundleAsInProject("net.openhft","compiler"), + mavenBundleAsInProject("net.openhft","collections"), + mavenBundleAsInProject("net.openhft","lang"), + workspaceBundle("lang-test"), + junitBundles(), + systemPackage("sun.misc"), + systemPackage("sun.nio.ch"), + systemPackage("com.sun.jna"), + systemPackage("com.sun.jna.ptr"), + systemPackage("com.sun.tools.javac.api"), + cleanCaches() + ); + } + + @Test + public void checkInject() { + assertNotNull(context); + } + + @Test + public void checkBundle() { + Boolean bundleFound = false; + + Bundle[] bundles = context.getBundles(); + for (Bundle bundle : bundles) { + if (bundle != null) { + if (bundle.getSymbolicName().equals("net.openhft.lang")) { + bundleFound = true; + assertEquals(bundle.getState(),Bundle.ACTIVE); + } + } + } + + assertTrue(bundleFound); + } +} diff --git a/lang-test/src/test/java/net/openhft/lang/osgi/OSGiCollectionTest.java b/lang-test/src/test/java/net/openhft/lang/osgi/OSGiCollectionTest.java new file mode 100644 index 0000000..14d2eb9 --- /dev/null +++ b/lang-test/src/test/java/net/openhft/lang/osgi/OSGiCollectionTest.java @@ -0,0 +1,73 @@ +/* + * Copyright (C) 2015 higherfrequencytrading.com + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as published by + * the Free Software Foundation, either version 3 of the License. + * + * 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 Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with this program. If not, see <http://www.gnu.org/licenses/>. + */ + +package net.openhft.lang.osgi; + +import net.openhft.lang.collection.HugeArray; +import net.openhft.lang.collection.HugeCollections; +import net.openhft.langosgi.model.JavaBeanInterface; +import org.junit.Ignore; +import org.junit.Test; +import org.junit.runner.RunWith; +import org.ops4j.pax.exam.Configuration; +import org.ops4j.pax.exam.Option; +import org.ops4j.pax.exam.junit.PaxExam; +import org.osgi.framework.BundleContext; + +import javax.inject.Inject; + +import static org.junit.Assert.assertNotNull; +import static org.ops4j.pax.exam.CoreOptions.*; + +/** + * @author lburgazzoli + */ +@RunWith(PaxExam.class) +public class OSGiCollectionTest extends OSGiTestBase { + @Inject + BundleContext context; + + @Configuration + public Option[] config() { + return options( + systemProperty("org.osgi.framework.storage.clean").value("true"), + systemProperty("org.ops4j.pax.logging.DefaultServiceLog.level").value("WARN"), + mavenBundleAsInProject("org.slf4j","slf4j-api"), + mavenBundleAsInProject("org.slf4j","slf4j-simple").noStart(), + mavenBundleAsInProject("net.openhft","affinity"), + mavenBundleAsInProject("net.openhft","compiler"), + mavenBundleAsInProject("net.openhft","collections"), + mavenBundleAsInProject("net.openhft","lang"), + workspaceBundle("lang-test"), + junitBundles(), + systemPackage("sun.misc"), + systemPackage("sun.nio.ch"), + systemPackage("com.sun.jna"), + systemPackage("com.sun.jna.ptr"), + systemPackage("com.sun.tools.javac.api"), + cleanCaches() + ); + } + + @Test + @Ignore + public void checkHugeArray() { + int length = 10 * 1000 * 1000; + HugeArray<JavaBeanInterface> array = HugeCollections.newArray(JavaBeanInterface.class, length); + + assertNotNull(array); + } +} diff --git a/lang-test/src/test/java/net/openhft/lang/osgi/OSGiTestBase.java b/lang-test/src/test/java/net/openhft/lang/osgi/OSGiTestBase.java new file mode 100644 index 0000000..79bd259 --- /dev/null +++ b/lang-test/src/test/java/net/openhft/lang/osgi/OSGiTestBase.java @@ -0,0 +1,54 @@ +/* + * Copyright (C) 2015 higherfrequencytrading.com + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as published by + * the Free Software Foundation, either version 3 of the License. + * + * 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 Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with this program. If not, see <http://www.gnu.org/licenses/>. + */ +package net.openhft.lang.osgi; + +import org.ops4j.pax.exam.CoreOptions; +import org.ops4j.pax.exam.Option; +import org.ops4j.pax.exam.options.MavenArtifactProvisionOption; + +import java.io.File; + +/** + * + */ +public class OSGiTestBase { + /** + * + * @param projectName + * @return + */ + public static Option workspaceBundle(String projectName) { + String baseDir = System.getProperty("main.basedir"); + String bundleDir = null; + + bundleDir = String.format("%s/%s/target/classes",baseDir,projectName); + if(new File(bundleDir).exists()) { + return CoreOptions.bundle(String.format("reference:file:%s", bundleDir)); + } + + bundleDir = String.format("%s/../%s/target/classes",baseDir,projectName); + if(new File(bundleDir).exists()) { + return CoreOptions.bundle(String.format("reference:file:%s", bundleDir)); + } + + return null; + } + + public static MavenArtifactProvisionOption mavenBundleAsInProject(final String groupId,final String artifactId) { + return CoreOptions.mavenBundle().groupId(groupId).artifactId(artifactId).versionAsInProject(); + } +} + diff --git a/lang/pom.xml b/lang/pom.xml index 209a61a..b3210ab 100755 --- a/lang/pom.xml +++ b/lang/pom.xml @@ -1,146 +1,141 @@ <?xml version="1.0" encoding="UTF-8"?> <!-- - ~ Copyright 2013 Peter Lawrey + ~ Copyright (C) 2015 higherfrequencytrading.com ~ - ~ Licensed under the Apache License, Version 2.0 (the "License"); - ~ you may not use this file except in compliance with the License. - ~ You may obtain a copy of the License at + ~ This program is free software: you can redistribute it and/or modify + ~ it under the terms of the GNU Lesser General Public License as published by + ~ the Free Software Foundation, either version 3 of the License. ~ - ~ http://www.apache.org/licenses/LICENSE-2.0 + ~ 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 Lesser General Public License for more details. ~ - ~ Unless required by applicable law or agreed to in writing, software - ~ distributed under the License is distributed on an "AS IS" BASIS, - ~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - ~ See the License for the specific language governing permissions and - ~ limitations under the License. + ~ You should have received a copy of the GNU Lesser General Public License + ~ along with this program. If not, see <http://www.gnu.org/licenses />. --> -<project xmlns="http://maven.apache.org/POM/4.0.0" - xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" - xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd"> - <modelVersion>4.0.0</modelVersion> +<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd"> + <parent> + <groupId>net.openhft</groupId> + <artifactId>java-parent-pom</artifactId> + <version>1.1.10</version> + <relativePath /> + </parent> - <groupId>net.openhft</groupId> + <modelVersion>4.0.0</modelVersion> <artifactId>lang</artifactId> - <version>6.1.4-SNAPSHOT</version> + <version>6.7.6</version> <packaging>bundle</packaging> - <name>OpenHFT/Java-Lang/lang</name> <description>Java Lang library for High Frequency Trading (Java 6+)</description> - <properties> - <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding> - <felix.version>3.2.2</felix.version> - <pax.exam.version>3.3.0</pax.exam.version> - <pax.url.version>1.6.0</pax.url.version> - </properties> + <dependencyManagement> + <dependencies> + <dependency> + <groupId>net.openhft</groupId> + <artifactId>third-party-bom</artifactId> + <type>pom</type> + <version>3.4.20</version> + <scope>import</scope> + </dependency> + + <dependency> + <groupId>net.openhft</groupId> + <artifactId>chronicle-bom</artifactId> + <version>1.10.35</version> + <type>pom</type> + <scope>import</scope> + </dependency> + + </dependencies> + </dependencyManagement> + <dependencies> + + <dependency> + <groupId>org.ow2.asm</groupId> + <artifactId>asm</artifactId> + </dependency> + <dependency> - <groupId>org.kohsuke.jetbrains</groupId> - <artifactId>annotations</artifactId> - <version>9.0</version> - <scope>compile</scope> + <groupId>org.slf4j</groupId> + <artifactId>slf4j-api</artifactId> + </dependency> + + <dependency> + <groupId>com.sun.java</groupId> + <artifactId>tools</artifactId> </dependency> <dependency> <groupId>net.openhft</groupId> <artifactId>compiler</artifactId> - <version>2.1</version> - <optional>true</optional> + </dependency> + + <dependency> + <groupId>org.xerial.snappy</groupId> + <artifactId>snappy-java</artifactId> </dependency> <dependency> <groupId>org.easymock</groupId> <artifactId>easymock</artifactId> - <version>3.1</version> <scope>test</scope> </dependency> + <dependency> <groupId>junit</groupId> <artifactId>junit</artifactId> - <version>4.11</version> <scope>test</scope> </dependency> + + <dependency> + <groupId>org.slf4j</groupId> + <artifactId>slf4j-simple</artifactId> + <scope>test</scope> + </dependency> + </dependencies> <build> + <plugins> + <plugin> <groupId>org.apache.maven.plugins</groupId> - <artifactId>maven-compiler-plugin</artifactId> - <version>3.1</version> - <configuration> - <source>1.6</source> - <target>1.6</target> - <encoding>UTF-8</encoding> - </configuration> - </plugin> - <plugin> - <groupId>org.apache.maven.plugins</groupId> - <artifactId>maven-source-plugin</artifactId> - <version>2.2</version> - <executions> - <execution> - <id>attach-sources</id> - <phase>verify</phase> - <goals> - <goal>jar</goal> - </goals> - </execution> - </executions> - </plugin> - <plugin> - <groupId>org.apache.maven.plugins</groupId> - <artifactId>maven-javadoc-plugin</artifactId> - <version>2.9</version> - <executions> - <execution> - <id>attach-javadocs</id> - <phase>verify</phase> - <goals> - <goal>jar</goal> - </goals> - </execution> - </executions> + <artifactId>maven-scm-publish-plugin</artifactId> <configuration> - <show>public</show> - <nohelp>true</nohelp> + <checkoutDirectory>${project.build.directory}/scmpublish/javadoc + </checkoutDirectory> + <checkinComment>Publishing javadoc for ${project.artifactId}:${project.version} + </checkinComment> + <content>${project.reporting.outputDirectory}</content> + <skipDeletedFiles>true</skipDeletedFiles> + <pubScmUrl>scm:git:git@github.com:OpenHFT/Java-Lang</pubScmUrl> + <scmBranch>gh-pages</scmBranch> </configuration> </plugin> - <!-- plugin> - <groupId>org.apache.maven.plugins</groupId> - <artifactId>maven-gpg-plugin</artifactId> - <version>1.4</version> - <configuration> - <passphrase>${gpg.passphrase}</passphrase> - </configuration> - <executions> - <execution> - <id>sign-artifacts</id> - <phase>verify</phase> - <goals> - <goal>sign</goal> - </goals> - </execution> - </executions> - </plugin --> - <plugin> <groupId>org.apache.felix</groupId> <artifactId>maven-bundle-plugin</artifactId> - <version>2.3.7</version> <extensions>true</extensions> <configuration> <instructions> - <Bundle-SymbolicName>${project.groupId}.${project.artifactId}</Bundle-SymbolicName> - <Bundle-Name>${project.artifactId}</Bundle-Name> + <Bundle-SymbolicName>${project.groupId}.${project.artifactId} + </Bundle-SymbolicName> + <Bundle-Name>OpenHFT :: ${project.artifactId}</Bundle-Name> + <Bundle-Version>${project.version}</Bundle-Version> + <Export-Package> + net.openhft.lang.* + </Export-Package> </instructions> </configuration> <executions> - <!-- - This execution makes sure that the manifest is available - when the tests are executed + <!-- + This execution makes sure that the manifest is available + when the tests are executed --> <execution> <goals> @@ -149,27 +144,69 @@ </execution> </executions> </plugin> + + <plugin> + <groupId>org.apache.maven.plugins</groupId> + <artifactId>maven-compiler-plugin</artifactId> + <configuration> + <source>1.6</source> + <target>1.6</target> + <encoding>UTF-8</encoding> + </configuration> + </plugin> </plugins> </build> - <url>http://www.openhft.net</url> - <licenses> - <license> - <name>The Apache Software License, Version 2.0</name> - <url>http://www.apache.org/licenses/LICENSE-2.0.txt</url> - <distribution>repo</distribution> - <comments>A business-friendly OSS license</comments> - </license> - </licenses> - - <developers> - <developer> - <name>Peter Lawrey</name> - <email>peter.lawrey@higherfrequencytrading.com</email> - </developer> - </developers> + <profiles> + <profile> + <id>local</id> + <properties> + <localurl>http://localhost:8080</localurl> + </properties> + </profile> + + <profile> + <!-- to use this run with the profile --> + <id>pitest</id> + <build> + <plugins> + <plugin> + <!-- For debugging in IntelliJ IDEA --> + <groupId>org.apache.maven.plugins</groupId> + <artifactId>maven-surefire-plugin</artifactId> + <version>2.18.1</version> + <configuration> + <systemPropertyVariables> + <project.build.directory>${project.build.directory} + </project.build.directory> + </systemPropertyVariables> + + <forkMode>never</forkMode> + </configuration> + </plugin> + <plugin> + <groupId>org.pitest</groupId> + <artifactId>pitest-maven</artifactId> + <version>0.33</version> + <configuration> + <targetClasses> + <param>net.openhft.lang.collection.*</param> + </targetClasses> + <targetTests> + <param>net.openhft.lang.collection.*</param> + </targetTests> + </configuration> + </plugin> + </plugins> + </build> + </profile> + </profiles> <scm> - <url>scm:git:https://github.com/OpenHFT/Java-Lang.git</url> + <url>scm:git:git@github.com:OpenHFT/Java-Lang.git</url> + <connection>scm:git:git@github.com:OpenHFT/Java-Lang.git</connection> + <developerConnection>scm:git:git@github.com:OpenHFT/Java-Lang.git</developerConnection> + <tag>lang-6.7.6</tag> </scm> -</project>
\ No newline at end of file + +</project> diff --git a/lang/src/main/java/net/openhft/lang/Compare.java b/lang/src/main/java/net/openhft/lang/Compare.java index 8d61054..c3c97ae 100644 --- a/lang/src/main/java/net/openhft/lang/Compare.java +++ b/lang/src/main/java/net/openhft/lang/Compare.java @@ -1,17 +1,17 @@ /* - * Copyright 2013 Peter Lawrey + * Copyright (C) 2015 higherfrequencytrading.com * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as published by + * the Free Software Foundation, either version 3 of the License. * - * http://www.apache.org/licenses/LICENSE-2.0 + * 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 Lesser General Public License for more details. * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. + * You should have received a copy of the GNU Lesser General Public License + * along with this program. If not, see <http://www.gnu.org/licenses/>. */ package net.openhft.lang; @@ -27,7 +27,7 @@ public abstract class Compare { return a == b; } - public static long calcLongHashCode(boolean a) { + private static long calcLongHashCode(boolean a) { return a ? 1 : 0; } @@ -43,7 +43,7 @@ public abstract class Compare { return a == b; } - public static long calcLongHashCode(byte a) { + private static long calcLongHashCode(byte a) { return a; } @@ -59,7 +59,7 @@ public abstract class Compare { return a == b; } - public static long calcLongHashCode(char a) { + private static long calcLongHashCode(char a) { return a; } @@ -75,7 +75,7 @@ public abstract class Compare { return a == b; } - public static long calcLongHashCode(short a) { + private static long calcLongHashCode(short a) { return a; } @@ -91,7 +91,7 @@ public abstract class Compare { return a == b; } - public static long calcLongHashCode(int a) { + private static long calcLongHashCode(int a) { return a; } @@ -107,7 +107,7 @@ public abstract class Compare { return a == b; } - public static long calcLongHashCode(long a) { + private static long calcLongHashCode(long a) { return a; } @@ -119,11 +119,11 @@ public abstract class Compare { return a == null ? NULL_HASHCODE : calcLongHashCode(a.longValue()); } - public static boolean isEqual(float a, float b) { + private static boolean isEqual(float a, float b) { return Float.floatToRawIntBits(a) == Float.floatToRawIntBits(b); } - public static long calcLongHashCode(float a) { + private static long calcLongHashCode(float a) { return Float.floatToRawIntBits(a); } @@ -135,11 +135,11 @@ public abstract class Compare { return a == null ? NULL_HASHCODE : calcLongHashCode(a.floatValue()); } - public static boolean isEqual(double a, double b) { + private static boolean isEqual(double a, double b) { return Double.doubleToRawLongBits(a) == Double.doubleToRawLongBits(b); } - public static long calcLongHashCode(double a) { + private static long calcLongHashCode(double a) { return Double.doubleToRawLongBits(a); } @@ -155,7 +155,7 @@ public abstract class Compare { return a == null ? b == null : b != null && a.equals(b); } - public static long calcLongHashCode(LongHashable t) { + private static long calcLongHashCode(LongHashable t) { return t.longHashCode(); } @@ -167,6 +167,8 @@ public abstract class Compare { } public static long calcLongHashCode(CharSequence s) { + if (s == null) + return NULL_HASHCODE; long hash = 0; for (int i = 0, len = s.length(); i < len; i++) { hash = 57 * hash + s.charAt(i); diff --git a/lang/src/main/java/net/openhft/lang/InterruptedRuntimeException.java b/lang/src/main/java/net/openhft/lang/InterruptedRuntimeException.java new file mode 100644 index 0000000..3a8dc05 --- /dev/null +++ b/lang/src/main/java/net/openhft/lang/InterruptedRuntimeException.java @@ -0,0 +1,23 @@ +/* + * Copyright (C) 2015 higherfrequencytrading.com + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as published by + * the Free Software Foundation, either version 3 of the License. + * + * 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 Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with this program. If not, see <http://www.gnu.org/licenses/>. + */ + +package net.openhft.lang; + +/** + * Like InterruptedException except unchecked + */ +public class InterruptedRuntimeException extends IllegalStateException { +} diff --git a/lang/src/main/java/net/openhft/lang/Jvm.java b/lang/src/main/java/net/openhft/lang/Jvm.java index 99e07ab..4169ac0 100755 --- a/lang/src/main/java/net/openhft/lang/Jvm.java +++ b/lang/src/main/java/net/openhft/lang/Jvm.java @@ -1,25 +1,31 @@ /* - * Copyright 2013 Peter Lawrey + * Copyright (C) 2015 higherfrequencytrading.com * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as published by + * the Free Software Foundation, either version 3 of the License. * - * http://www.apache.org/licenses/LICENSE-2.0 + * 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 Lesser General Public License for more details. * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. + * You should have received a copy of the GNU Lesser General Public License + * along with this program. If not, see <http://www.gnu.org/licenses/>. */ package net.openhft.lang; +import net.openhft.lang.io.NativeBytes; + import java.io.File; +import java.io.FileNotFoundException; import java.io.IOException; import java.lang.management.ManagementFactory; +import java.lang.reflect.Field; import java.util.Random; +import java.util.Scanner; +import java.util.logging.Level; import java.util.logging.Logger; /** @@ -27,7 +33,9 @@ import java.util.logging.Logger; */ public enum Jvm { ; - public static final String TMP = System.getProperty("java.io.tmpdir"); + + public static String TMP = System.getProperty("java.io.tmpdir"); + private static final boolean IS64BIT = is64Bit0(); public static boolean is64Bit() { @@ -58,17 +66,22 @@ public enum Jvm { String pid = null; final File self = new File("/proc/self"); try { - if (self.exists()) + if (self.exists()) { pid = self.getCanonicalFile().getName(); + } } catch (IOException ignored) { - // ignored } - if (pid == null) - pid = ManagementFactory.getRuntimeMXBean().getName().split("@")[0]; + + if (pid == null) { + pid = ManagementFactory.getRuntimeMXBean().getName().split("@", 0)[0]; + } + if (pid == null) { int rpid = new Random().nextInt(1 << 16); - Logger.getLogger(Jvm.class.getName()).warning("Unable to determine PID, picked a random number=" + rpid); + LoggerHolder.LOGGER.log(Level.WARNING, "Unable to determine PID, picked a random number=" + rpid); + return rpid; + } else { return Integer.parseInt(pid); } @@ -84,7 +97,144 @@ public enum Jvm { } public static long getUniqueTid(Thread thread) { - return (long) getProcessId() << 32 | (thread.getId() & 0xFFFFFFFFL); + // Assume 48 bit for 16 to 24-bit process id and 16 million threads from the start. + return ((long) getProcessId() << 24) | thread.getId(); + } + + private static final String OS = System.getProperty("os.name").toLowerCase(); + + public static boolean isWindows() { + return OS.startsWith("win"); + } + + public static boolean isMacOSX() { + return OS.contains("mac"); + } + + public static boolean isLinux() { + return OS.startsWith("linux"); + } + + public static boolean isUnix() { + return OS.contains("nix") || + OS.contains("nux") || + OS.contains("aix") || + OS.contains("bsd") || + OS.contains("sun") || + OS.contains("hpux"); + } + + public static boolean isSolaris() { + return OS.startsWith("sun"); + } + + public static final int PID_BITS = Maths.intLog2(getPidMax()); + + public static long getPidMax() { + if (isLinux()) { + File file = new File("/proc/sys/kernel/pid_max"); + if (file.canRead()) { + try { + return Maths.nextPower2(new Scanner(file).nextLong(), 1); + } catch (FileNotFoundException e) { + LoggerHolder.LOGGER.log(Level.WARNING, "", e); + } + } + } else if (isMacOSX()) { + return 1L << 24; + } + + // the default. + return 1L << 16; + } + + private static String convertStreamToString(java.io.InputStream is) { + java.util.Scanner s = new java.util.Scanner(is).useDelimiter("\\A"); + return s.hasNext() ? s.next() : ""; + } + + public static long freePhysicalMemoryOnWindowsInBytes() throws IOException { + if (!isWindows()) { + throw new IllegalStateException("Method freePhysicalMemoryOnWindowsInBytes() should " + + "be called only on windows. Use Jvm.isWindows() to check the OS."); + } + + Process pr = Runtime.getRuntime().exec("wmic OS get FreePhysicalMemory /Value"); + try { + int result = pr.waitFor(); + String output = convertStreamToString(pr.getInputStream()); + if (result != 0) { + String errorOutput = convertStreamToString(pr.getErrorStream()); + throw new IOException("Couldn't get free physical memory on windows. " + + "Command \"wmic OS get FreePhysicalMemory /Value\" exited with " + + result + " code, output: \"" + output + "\", error output: \"" + + errorOutput + "\""); + } + String[] parts = output.trim().split("="); + if (parts.length != 2) { + throw new IOException("Couldn't get free physical memory on windows. " + + "Command \"wmic OS get FreePhysicalMemory /Value\" output has unexpected " + + "format: \"" + output + "\""); + } + try { + return MemoryUnit.KILOBYTES.toBytes(Long.parseLong(parts[1])); + } catch (NumberFormatException e) { + throw new IOException("Couldn't get free physical memory on windows. " + + "Command \"wmic OS get FreePhysicalMemory /Value\" output has unexpected " + + "format: \"" + output + "\"", e); + } + } catch (InterruptedException e) { + throw new IOException(e); + } + } + + public static void checkInterrupted() { + if (Thread.currentThread().isInterrupted()) throw new InterruptedRuntimeException(); + } + + /** + * Utility method to support throwing checked exceptions out of the streams API + * + * @param t the exception to rethrow + * @return the exception + */ + public static RuntimeException rethrow(Throwable t) { + NativeBytes.UNSAFE.throwException(t); + throw new AssertionError(); + } + + static class LoggerHolder { + public static final Logger LOGGER = Logger.getLogger(Jvm.class.getName()); + } + + public static void trimStackTrace(StringBuilder sb, StackTraceElement... stes) { + int first = trimFirst(stes); + int last = trimLast(first, stes); + for (int i = first; i <= last; i++) + sb.append("\n\tat ").append(stes[i]); + } + + private static int trimFirst(StackTraceElement[] stes) { + int first = 0; + for (; first < stes.length; first++) + if (!isInternal(stes[first].getClassName())) + break; + if (first > 0) first--; + if (first > 0) first--; + return first; + } + + private static int trimLast(int first, StackTraceElement[] stes) { + int last = stes.length - 1; + for (; first < last; last--) + if (!isInternal(stes[last].getClassName())) + break; + if (last < stes.length - 1) last++; + return last; + } + + public static boolean isInternal(String className) { + return className.startsWith("jdk.") || className.startsWith("sun.") || className.startsWith("java."); } } diff --git a/lang/src/main/java/net/openhft/lang/LongHashable.java b/lang/src/main/java/net/openhft/lang/LongHashable.java index 508ac7e..92a6d55 100644 --- a/lang/src/main/java/net/openhft/lang/LongHashable.java +++ b/lang/src/main/java/net/openhft/lang/LongHashable.java @@ -1,17 +1,17 @@ /* - * Copyright 2013 Peter Lawrey + * Copyright (C) 2015 higherfrequencytrading.com * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as published by + * the Free Software Foundation, either version 3 of the License. * - * http://www.apache.org/licenses/LICENSE-2.0 + * 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 Lesser General Public License for more details. * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. + * You should have received a copy of the GNU Lesser General Public License + * along with this program. If not, see <http://www.gnu.org/licenses/>. */ package net.openhft.lang; diff --git a/lang/src/main/java/net/openhft/lang/Maths.java b/lang/src/main/java/net/openhft/lang/Maths.java index f293677..a89d790 100755 --- a/lang/src/main/java/net/openhft/lang/Maths.java +++ b/lang/src/main/java/net/openhft/lang/Maths.java @@ -1,17 +1,17 @@ /* - * Copyright 2013 Peter Lawrey + * Copyright (C) 2015 higherfrequencytrading.com * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as published by + * the Free Software Foundation, either version 3 of the License. * - * http://www.apache.org/licenses/LICENSE-2.0 + * 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 Lesser General Public License for more details. * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. + * You should have received a copy of the GNU Lesser General Public License + * along with this program. If not, see <http://www.gnu.org/licenses/>. */ package net.openhft.lang; @@ -23,7 +23,7 @@ public class Maths { /** * Numbers larger than this are whole numbers due to representation error. */ - public static final double WHOLE_NUMBER = 1L << 53; + private static final double WHOLE_NUMBER = 1L << 53; private static final long[] TENS = new long[19]; static { @@ -38,6 +38,7 @@ public class Maths { * precision of double. * * @param d value to round + * @return rounded value */ public static double round2(double d) { final double factor = 1e2; @@ -51,6 +52,7 @@ public class Maths { * precision of double. * * @param d value to round + * @return rounded value */ public static double round4(double d) { final double factor = 1e4; @@ -64,6 +66,7 @@ public class Maths { * precision of double. * * @param d value to round + * @return rounded value */ public static double round6(double d) { final double factor = 1e6; @@ -77,6 +80,7 @@ public class Maths { * precision of double. * * @param d value to round + * @return rounded value */ public static double round8(double d) { final double factor = 1e8; @@ -90,8 +94,11 @@ public class Maths { } public static int nextPower2(int n, int min) { + if (!isPowerOf2(min)) + throw new IllegalArgumentException(); if (n < min) return min; - if ((n & (n - 1)) == 0) return n; + if (isPowerOf2(n)) + return n; int i = min; while (i < n) { i *= 2; @@ -101,8 +108,11 @@ public class Maths { } public static long nextPower2(long n, long min) { + if (!isPowerOf2(min)) + throw new IllegalArgumentException(); if (n < min) return min; - if ((n & (n - 1)) == 0) return n; + if (isPowerOf2(n)) + return n; long i = min; while (i < n) { i *= 2; @@ -111,17 +121,24 @@ public class Maths { return i; } + public static boolean isPowerOf2(int n) { + return (n & (n - 1)) == 0; + } + + public static boolean isPowerOf2(long n) { + return (n & (n - 1L)) == 0L; + } public static int hash(int n) { - n ^= (n >> 21) ^ (n >> 11); - n ^= (n >> 7) ^ (n >> 4); + n ^= (n >>> 21) - (n >>> 11); + n ^= (n >>> 7) + (n >>> 4); return n; } - public static int hash(long n) { - n ^= (n >> 43) ^ (n >> 21); - n ^= (n >> 15) ^ (n >> 7); - return (int) n; + public static long hash(long n) { + n ^= (n >>> 41) - (n >>> 21); + n ^= (n >>> 15) + (n >>> 7); + return n; } public static long hash(CharSequence cs) { @@ -131,7 +148,6 @@ public class Maths { return hash; } - /** * Compares two {@code long} values numerically. The value returned is identical to what would be returned by: * <pre> @@ -141,7 +157,7 @@ public class Maths { * @param x the first {@code long} to compare * @param y the second {@code long} to compare * @return the value {@code 0} if {@code x == y}; a value less than {@code 0} if {@code x < y}; and a value greater - * than {@code 0} if {@code x > y} + * than {@code 0} if {@code x > y} */ public static int compare(long x, long y) { return (x < y) ? -1 : ((x == y) ? 0 : 1); @@ -151,4 +167,16 @@ public class Maths { long l = Double.doubleToRawLongBits(num); return (int) ((l >> 52) - 1023); } + + public static int toInt(long l, String error) { + if (l < Integer.MIN_VALUE || l > Integer.MAX_VALUE) + throw new IllegalStateException(String.format(error, l)); + return (int) l; + } + + public static long agitate(long l) { + l ^= l >> 23; + l += Long.rotateRight(l, 18); + return l; + } } diff --git a/lang/src/main/java/net/openhft/lang/MemoryUnit.java b/lang/src/main/java/net/openhft/lang/MemoryUnit.java new file mode 100644 index 0000000..51830ae --- /dev/null +++ b/lang/src/main/java/net/openhft/lang/MemoryUnit.java @@ -0,0 +1,450 @@ +/* + * Copyright (C) 2015 higherfrequencytrading.com + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as published by + * the Free Software Foundation, either version 3 of the License. + * + * 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 Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with this program. If not, see <http://www.gnu.org/licenses/>. + */ + +/* + * Based on java.util.concurrent.TimeUnit, which is + * Written by Doug Lea with assistance from members of JCP JSR-166 + * Expert Group and released to the public domain, as explained at + * http://creativecommons.org/publicdomain/zero/1.0/ + */ + +package net.openhft.lang; + +import java.util.concurrent.TimeUnit; + +/** + * A {@code MemoryUnit} represents memory amounts at a given unit of + * granularity and provides utility methods to convert across units. A + * {@code MemoryUnit} does not maintain memory information, but only + * helps organize and use memory amounts representations that may be maintained + * separately across various contexts. + * + * <p>Note than in this class kilo-, mega- and giga- prefixes means 2^10 = 1024 multiplexing, + * that is more common in low-level programming, CPU and operation system contexts, + * not 1000 as defined by International System of Units (SI). + * + * <p>A {@code MemoryUnit} is mainly used to inform memory amount-based methods + * how a given memory amount parameter should be interpreted. + * + * <p>API of {@code MemoryUnit} is copied from {@link TimeUnit} enum. + */ +public enum MemoryUnit { + + /** + * Memory unit representing one bit. + */ + BITS { + @Override public long toBits(long a) { return a; } + @Override public long toBytes(long a) { return a/(C1/C0); } + @Override public long toLongs(long a) { return a/(C2/C0); } + @Override public long toCacheLines(long a) { return a/(C3/C0); } + @Override public long toKilobytes(long a) { return a/(C4/C0); } + @Override public long toPages(long a) { return a/(C5/C0); } + @Override public long toMegabytes(long a) { return a/(C6/C0); } + @Override public long toGigabytes(long a) { return a/(C7/C0); } + @Override public long convert(long a, MemoryUnit u) { return u.toBits(a); } + + @Override long alignToBytes(long a) { return y(a, C1/C0); } + @Override long alignToLongs(long a) { return y(a, C2/C0); } + @Override long alignToCacheLines(long a) { return y(a, C3/C0); } + @Override long alignToKilobytes(long a) { return y(a, C4/C0); } + @Override long alignToPages(long a) { return y(a, C5/C0); } + @Override long alignToMegabytes(long a) { return y(a, C6/C0); } + @Override long alignToGigabytes(long a) { return y(a, C7/C0); } + @Override public long align(long a, MemoryUnit u) { return ise(u, this); } + }, + + /** + * Memory unit representing one byte, i. e. 8 bits. + */ + BYTES { + @Override public long toBits(long a) { return x(a, C1/C0, MAX/(C1/C0)); } + @Override public long toBytes(long a) { return a; } + @Override public long toLongs(long a) { return a/(C2/C1); } + @Override public long toCacheLines(long a) { return a/(C3/C1); } + @Override public long toKilobytes(long a) { return a/(C4/C1); } + @Override public long toPages(long a) { return a/(C5/C1); } + @Override public long toMegabytes(long a) { return a/(C6/C1); } + @Override public long toGigabytes(long a) { return a/(C7/C1); } + @Override public long convert(long a, MemoryUnit u) { return u.toBytes(a); } + + @Override long alignToBytes(long a) { return ise(this, BYTES); } + @Override long alignToLongs(long a) { return y(a, C2/C1); } + @Override long alignToCacheLines(long a) { return y(a, C3/C1); } + @Override long alignToKilobytes(long a) { return y(a, C4/C1); } + @Override long alignToPages(long a) { return y(a, C5/C1); } + @Override long alignToMegabytes(long a) { return y(a, C6/C1); } + @Override long alignToGigabytes(long a) { return y(a, C7/C1); } + @Override public long align(long a, MemoryUnit u) { return u.alignToBytes(a); } + }, + + /** + * Memory unit representing 8 bytes, i. e. 64-bit word, + * the width of Java's primitive {@code long} type. + */ + LONGS { + @Override public long toBits(long a) { return x(a, C2/C0, MAX/(C2/C0)); } + @Override public long toBytes(long a) { return x(a, C2/C1, MAX/(C2/C1)); } + @Override public long toLongs(long a) { return a; } + @Override public long toCacheLines(long a) { return a/(C3/C2); } + @Override public long toKilobytes(long a) { return a/(C4/C2); } + @Override public long toPages(long a) { return a/(C5/C2); } + @Override public long toMegabytes(long a) { return a/(C6/C2); } + @Override public long toGigabytes(long a) { return a/(C7/C2); } + @Override public long convert(long a, MemoryUnit u) { return u.toLongs(a); } + + @Override long alignToBytes(long a) { return ise(this, BYTES); } + @Override long alignToLongs(long a) { return ise(this, LONGS); } + @Override long alignToCacheLines(long a) { return y(a, C3/C2); } + @Override long alignToKilobytes(long a) { return y(a, C4/C2); } + @Override long alignToPages(long a) { return y(a, C5/C2); } + @Override long alignToMegabytes(long a) { return y(a, C6/C2); } + @Override long alignToGigabytes(long a) { return y(a, C7/C2); } + @Override public long align(long a, MemoryUnit u) { return u.alignToLongs(a); } + }, + + /** + * Memory unit representing 64 bytes, i. e. the most common CPU cache line size. + */ + CACHE_LINES { + @Override public long toBits(long a) { return x(a, C3/C0, MAX/(C3/C0)); } + @Override public long toBytes(long a) { return x(a, C3/C1, MAX/(C3/C1)); } + @Override public long toLongs(long a) { return x(a, C3/C2, MAX/(C3/C2)); } + @Override public long toCacheLines(long a) { return a; } + @Override public long toKilobytes(long a) { return a/(C4/C3); } + @Override public long toPages(long a) { return a/(C5/C3); } + @Override public long toMegabytes(long a) { return a/(C6/C3); } + @Override public long toGigabytes(long a) { return a/(C7/C3); } + @Override public long convert(long a, MemoryUnit u) { return u.toCacheLines(a); } + + @Override long alignToBytes(long a) { return ise(this, BYTES); } + @Override long alignToLongs(long a) { return ise(this, LONGS); } + @Override long alignToCacheLines(long a) { return ise(this, CACHE_LINES); } + @Override long alignToKilobytes(long a) { return y(a, C4/C3); } + @Override long alignToPages(long a) { return y(a, C5/C3); } + @Override long alignToMegabytes(long a) { return y(a, C6/C3); } + @Override long alignToGigabytes(long a) { return y(a, C7/C3); } + @Override public long align(long a, MemoryUnit u) { return u.alignToCacheLines(a); } + }, + + /** + * Memory unit representing 1024 bytes. + */ + KILOBYTES { + @Override public long toBits(long a) { return x(a, C4/C0, MAX/(C4/C0)); } + @Override public long toBytes(long a) { return x(a, C4/C1, MAX/(C4/C1)); } + @Override public long toLongs(long a) { return x(a, C4/C2, MAX/(C4/C2)); } + @Override public long toCacheLines(long a) { return x(a, C4/C3, MAX/(C4/C3)); } + @Override public long toKilobytes(long a) { return a; } + @Override public long toPages(long a) { return a/(C5/C4); } + @Override public long toMegabytes(long a) { return a/(C6/C4); } + @Override public long toGigabytes(long a) { return a/(C7/C4); } + @Override public long convert(long a, MemoryUnit u) { return u.toKilobytes(a); } + + @Override long alignToBytes(long a) { return ise(this, BYTES); } + @Override long alignToLongs(long a) { return ise(this, LONGS); } + @Override long alignToCacheLines(long a) { return ise(this, CACHE_LINES); } + @Override long alignToKilobytes(long a) { return ise(this, KILOBYTES); } + @Override long alignToPages(long a) { return y(a, C5/C4); } + @Override long alignToMegabytes(long a) { return y(a, C6/C4); } + @Override long alignToGigabytes(long a) { return y(a, C7/C4); } + @Override public long align(long a, MemoryUnit u) { return u.alignToKilobytes(a); } + }, + + /** + * Memory unit representing 4096 bytes, i. e. the most common native memory page size. + */ + PAGES { + @Override public long toBits(long a) { return x(a, C5/C0, MAX/(C5/C0)); } + @Override public long toBytes(long a) { return x(a, C5/C1, MAX/(C5/C1)); } + @Override public long toLongs(long a) { return x(a, C5/C2, MAX/(C5/C2)); } + @Override public long toCacheLines(long a) { return x(a, C5/C3, MAX/(C5/C3)); } + @Override public long toKilobytes(long a) { return x(a, C5/C4, MAX/(C5/C4)); } + @Override public long toPages(long a) { return a; } + @Override public long toMegabytes(long a) { return a/(C6/C5); } + @Override public long toGigabytes(long a) { return a/(C7/C5); } + @Override public long convert(long a, MemoryUnit u) { return u.toPages(a); } + + @Override long alignToBytes(long a) { return ise(this, BYTES); } + @Override long alignToLongs(long a) { return ise(this, LONGS); } + @Override long alignToCacheLines(long a) { return ise(this, CACHE_LINES); } + @Override long alignToKilobytes(long a) { return ise(this, KILOBYTES); } + @Override long alignToPages(long a) { return ise(this, PAGES); } + @Override long alignToMegabytes(long a) { return y(a, C6/C5); } + @Override long alignToGigabytes(long a) { return y(a, C7/C5); } + @Override public long align(long a, MemoryUnit u) { return u.alignToPages(a); } + }, + + /** + * Memory unit representing 1024 kilobytes. + */ + MEGABYTES { + @Override public long toBits(long a) { return x(a, C6/C0, MAX/(C6/C0)); } + @Override public long toBytes(long a) { return x(a, C6/C1, MAX/(C6/C1)); } + @Override public long toLongs(long a) { return x(a, C6/C2, MAX/(C6/C2)); } + @Override public long toCacheLines(long a) { return x(a, C6/C3, MAX/(C6/C3)); } + @Override public long toKilobytes(long a) { return x(a, C6/C4, MAX/(C6/C4)); } + @Override public long toPages(long a) { return x(a, C6/C5, MAX/(C6/C5)); } + @Override public long toMegabytes(long a) { return a; } + @Override public long toGigabytes(long a) { return a/(C7/C6); } + @Override public long convert(long a, MemoryUnit u) { return u.toMegabytes(a); } + + @Override long alignToBytes(long a) { return ise(this, BYTES); } + @Override long alignToLongs(long a) { return ise(this, LONGS); } + @Override long alignToCacheLines(long a) { return ise(this, CACHE_LINES); } + @Override long alignToKilobytes(long a) { return ise(this, KILOBYTES); } + @Override long alignToPages(long a) { return ise(this, PAGES); } + @Override long alignToMegabytes(long a) { return ise(this, MEGABYTES); } + @Override long alignToGigabytes(long a) { return y(a, C7/C6); } + @Override public long align(long a, MemoryUnit u) { return u.alignToMegabytes(a); } + }, + + /** + * Memory unit representing 1024 megabytes. + */ + GIGABYTES { + @Override public long toBits(long a) { return x(a, C7/C0, MAX/(C7/C0)); } + @Override public long toBytes(long a) { return x(a, C7/C1, MAX/(C7/C1)); } + @Override public long toLongs(long a) { return x(a, C7/C2, MAX/(C7/C2)); } + @Override public long toCacheLines(long a) { return x(a, C7/C3, MAX/(C7/C3)); } + @Override public long toKilobytes(long a) { return x(a, C7/C4, MAX/(C7/C4)); } + @Override public long toPages(long a) { return x(a, C7/C5, MAX/(C7/C5)); } + @Override public long toMegabytes(long a) { return x(a, C7/C6, MAX/(C7/C6)); } + @Override public long toGigabytes(long a) { return a; } + @Override public long convert(long a, MemoryUnit u) { return u.toGigabytes(a); } + + @Override long alignToBytes(long a) { return ise(this, BYTES); } + @Override long alignToLongs(long a) { return ise(this, LONGS); } + @Override long alignToCacheLines(long a) { return ise(this, CACHE_LINES); } + @Override long alignToKilobytes(long a) { return ise(this, KILOBYTES); } + @Override long alignToPages(long a) { return ise(this, PAGES); } + @Override long alignToMegabytes(long a) { return ise(this, MEGABYTES); } + @Override long alignToGigabytes(long a) { return ise(this, GIGABYTES); } + @Override public long align(long a, MemoryUnit u) { return u.alignToGigabytes(a); } + }; + + // Handy constants for conversion methods + static final long C0 = 1L; + static final long C1 = C0 * 8L; + static final long C2 = C1 * 8L; + static final long C3 = C2 * 8L; + static final long C4 = C3 * 16L; + static final long C5 = C4 * 4L; + static final long C6 = C5 * 256L; + static final long C7 = C6 * 1024L; + + static final long MAX = Long.MAX_VALUE; + + /** + * Scale d by m, checking for overflow. + * This has a short name to make above code more readable. + */ + static long x(long a, long m, long over) { + if (a > over) return Long.MAX_VALUE; + if (a < -over) return Long.MIN_VALUE; + return a * m; + } + + static long y(long amount, long align) { + if (amount == 0L) + return 0L; + long mask = ~(align - 1L); + if (amount > 0L) { + long filled = amount + align - 1L; + if (filled > 0L) { + return filled & mask; + + } else { + long maxAlignedLong = Long.MAX_VALUE & mask; + if (amount <= maxAlignedLong) + return maxAlignedLong; + } + } else { + // amount is negative + long filled = amount - align + 1L; + if (filled < 0L) { + return filled & mask; + + } else { + long minAlignedLong = Long.MIN_VALUE & mask; + if (amount >= minAlignedLong) + return minAlignedLong; + } + } + throw new IllegalArgumentException("Couldn't align " + amount + " by " + align); + } + + static long ise(MemoryUnit unitToAlign, MemoryUnit alignmentUnit) { + throw new IllegalStateException("Couldn't align " + unitToAlign + " by " + alignmentUnit); + } + + // To maintain full signature compatibility with 1.5, and to improve the + // clarity of the generated javadoc (see 6287639: Abstract methods in + // enum classes should not be listed as abstract), method convert + // etc. are not declared abstract but otherwise act as abstract methods. + + /** + * Converts the given memory amount in the given unit to this unit. + * Conversions from finer to coarser granularities truncate, so + * lose precision. For example, converting {@code 7} bits + * to bytes results in {@code 0}. Conversions from coarser to + * finer granularities with arguments that would numerically + * overflow saturate to {@code Long.MIN_VALUE} if negative or + * {@code Long.MAX_VALUE} if positive. + * + * <p>For example, to convert 4096 bytes to cache lines, use: + * {@code MemoryUnit.CACHE_LINES.convert(4096L, MemoryUnit.BYTES)} + * + * @param sourceAmount the memory amount in the given {@code sourceUnit} + * @param sourceUnit the unit of the {@code sourceAmount} argument + * @return the converted amount in this unit, + * or {@code Long.MIN_VALUE} if conversion would negatively + * overflow, or {@code Long.MAX_VALUE} if it would positively overflow. + */ + public long convert(long sourceAmount, MemoryUnit sourceUnit) { + throw new AbstractMethodError(); + } + + /** + * Aligns the given memory amount in the given unit to this unit. For example, aligning + * {@code 1000} bytes to kilobytes results in {@code 1024}. Negative values are aligned towards + * negative infinity: e. g. aligning {@code -5} longs to cache lines results in {@code -8}. + * + * @param amountToAlign the memory amount in the given {@code unit} + * @param unit the unit of the {@code amountToAlign} argument + * @return the aligned amount, still in the given unit + * @throws IllegalArgumentException if the given {@code unit} is finer than this unit, + * or if the aligned value overflows {@code long} bounds + */ + public long align(long amountToAlign, MemoryUnit unit) { + throw new AbstractMethodError(); + } + + /** + * Equivalent to {@code convert(align(sourceAmount, sourceUnit), sourceUnit)}. + * + * @param sourceAmount the memory amount in the given {@code sourceUnit} + * @param sourceUnit the unit of the {@code sourceAmount} argument + * @return the saturated converted amount in this unit + * @throws IllegalArgumentException if the given {@code sourceUnit} is finer than this unit, + * or if the aligned value overflows {@code long} bounds + */ + public long alignAndConvert(long sourceAmount, MemoryUnit sourceUnit) { + return convert(align(sourceAmount, sourceUnit), sourceUnit); + } + + /** + * Equivalent to + * {@link #convert(long, MemoryUnit) BITS.convert(amount, this)}. + * @param amount the amount + * @return the converted amount, + * or {@code Long.MIN_VALUE} if conversion would negatively + * overflow, or {@code Long.MAX_VALUE} if it would positively overflow. + */ + public long toBits(long amount) { + throw new AbstractMethodError(); + } + + /** + * Equivalent to + * {@link #convert(long, MemoryUnit) BYTES.convert(amount, this)}. + * @param amount the amount + * @return the converted amount, + * or {@code Long.MIN_VALUE} if conversion would negatively + * overflow, or {@code Long.MAX_VALUE} if it would positively overflow. + */ + public long toBytes(long amount) { + throw new AbstractMethodError(); + } + + /** + * Equivalent to + * {@link #convert(long, MemoryUnit) LONGS.convert(amount, this)}. + * @param amount the amount + * @return the converted amount, + * or {@code Long.MIN_VALUE} if conversion would negatively + * overflow, or {@code Long.MAX_VALUE} if it would positively overflow. + */ + public long toLongs(long amount) { + throw new AbstractMethodError(); + } + + /** + * Equivalent to + * {@link #convert(long, MemoryUnit) CACHE_LINES.convert(amount, this)}. + * @param amount the amount + * @return the converted amount, + * or {@code Long.MIN_VALUE} if conversion would negatively + * overflow, or {@code Long.MAX_VALUE} if it would positively overflow. + */ + public long toCacheLines(long amount) { + throw new AbstractMethodError(); + } + + /** + * Equivalent to + * {@link #convert(long, MemoryUnit) KILOBYTES.convert(amount, this)}. + * @param amount the amount + * @return the converted amount, + * or {@code Long.MIN_VALUE} if conversion would negatively + * overflow, or {@code Long.MAX_VALUE} if it would positively overflow. + */ + public long toKilobytes(long amount) { + throw new AbstractMethodError(); + } + + /** + * Equivalent to + * {@link #convert(long, MemoryUnit) PAGES.convert(amount, this)}. + * @param amount the amount + * @return the converted amount, + * or {@code Long.MIN_VALUE} if conversion would negatively + * overflow, or {@code Long.MAX_VALUE} if it would positively overflow. + */ + public long toPages(long amount) { + throw new AbstractMethodError(); + } + + /** + * Equivalent to + * {@link #convert(long, MemoryUnit) MEGABYTES.convert(amount, this)}. + * @param amount the amount + * @return the converted amount, + * or {@code Long.MIN_VALUE} if conversion would negatively + * overflow, or {@code Long.MAX_VALUE} if it would positively overflow. + */ + public long toMegabytes(long amount) { + throw new AbstractMethodError(); + } + + /** + * Equivalent to + * {@link #convert(long, MemoryUnit) GIGABYTES.convert(amount, this)}. + * @param amount the amount + * @return the converted amount + */ + public long toGigabytes(long amount) { + throw new AbstractMethodError(); + } + + abstract long alignToBytes(long amount); + abstract long alignToLongs(long amount); + abstract long alignToCacheLines(long amount); + abstract long alignToKilobytes(long amount); + abstract long alignToPages(long amount); + abstract long alignToMegabytes(long amount); + abstract long alignToGigabytes(long amount); +} diff --git a/lang/src/main/java/net/openhft/lang/ReferenceCounted.java b/lang/src/main/java/net/openhft/lang/ReferenceCounted.java new file mode 100644 index 0000000..c74d42d --- /dev/null +++ b/lang/src/main/java/net/openhft/lang/ReferenceCounted.java @@ -0,0 +1,25 @@ +/* + * Copyright (C) 2015 higherfrequencytrading.com + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as published by + * the Free Software Foundation, either version 3 of the License. + * + * 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 Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with this program. If not, see <http://www.gnu.org/licenses/>. + */ + +package net.openhft.lang; + +public interface ReferenceCounted { + void reserve(); + + boolean release(); + + int refCount(); +} diff --git a/lang/src/main/java/net/openhft/lang/collection/ATSDirectBitSet.java b/lang/src/main/java/net/openhft/lang/collection/ATSDirectBitSet.java new file mode 100644 index 0000000..5a8194e --- /dev/null +++ b/lang/src/main/java/net/openhft/lang/collection/ATSDirectBitSet.java @@ -0,0 +1,1160 @@ +/* + * Copyright (C) 2015 higherfrequencytrading.com + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as published by + * the Free Software Foundation, either version 3 of the License. + * + * 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 Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with this program. If not, see <http://www.gnu.org/licenses/>. + */ + +package net.openhft.lang.collection; + +import net.openhft.lang.io.Bytes; + +import static java.lang.Long.numberOfLeadingZeros; +import static java.lang.Long.numberOfTrailingZeros; +import static net.openhft.lang.collection.SingleThreadedDirectBitSet.*; + +/** + * DirectBitSet with input validations and ThreadSafe memory access. + */ +public class ATSDirectBitSet implements DirectBitSet { + private final Bytes bytes; + private final long longLength; + + private ATSDirectBitSet(Bytes bytes) { + this.bytes = bytes; + assert (bytes.capacity() & 7) == 0; + longLength = bytes.capacity() >> 3; + } + + public static DirectBitSet wrap(Bytes bytes) { + return new ATSDirectBitSet(bytes); + } + + private static long rightShiftOneFill(long l, long shift) { + return (l >> shift) | ~(ALL_ONES >>> shift); + } + + private static long leftShiftOneFill(long l, long shift) { + return (l << shift) | ((1L << shift) - 1L); + } + + private long readLong(long longIndex) { + return bytes.readLong(firstByte(longIndex)); + } + + private long readVolatileLong(long longIndex) { + return bytes.readVolatileLong(firstByte(longIndex)); + } + + @Override + public void reserve() { + bytes.reserve(); + } + + @Override + public boolean release() { + return bytes.release(); + } + + @Override + public int refCount() { + return bytes.refCount(); + } + + @Override + public DirectBitSet flip(long bitIndex) { + long longIndex = longWithThisBit(bitIndex); + if (bitIndex < 0 || longIndex >= longLength) + throw new IndexOutOfBoundsException(); + long byteIndex = firstByte(longIndex); + // only 6 lowest-order bits used, JLS 15.19 + long mask = singleBit(bitIndex); + while (true) { + long l = bytes.readVolatileLong(byteIndex); + long l2 = l ^ mask; + if (bytes.compareAndSwapLong(byteIndex, l, l2)) + return this; + } + } + + @Override + public DirectBitSet flip(long fromIndex, long exclusiveToIndex) { + long fromLongIndex = longWithThisBit(fromIndex); + long toIndex = exclusiveToIndex - 1; + long toLongIndex = longWithThisBit(toIndex); + if (fromIndex < 0 || fromIndex > exclusiveToIndex || + toLongIndex >= longLength) + throw new IndexOutOfBoundsException(); + + if (fromLongIndex != toLongIndex) { + long firstFullLongIndex = fromLongIndex; + if ((fromIndex & 63) != 0) { + long fromByteIndex = firstByte(fromLongIndex); + long mask = higherBitsIncludingThis(fromIndex); + while (true) { + long l = bytes.readVolatileLong(fromByteIndex); + long l2 = l ^ mask; + if (bytes.compareAndSwapLong(fromByteIndex, l, l2)) + break; + } + firstFullLongIndex++; + } + + if ((exclusiveToIndex & 63) == 0) { + for (long i = firstFullLongIndex; i <= toLongIndex; i++) { + while (true) { + long l = readVolatileLong(i); + long l2 = ~l; + if (bytes.compareAndSwapLong(firstByte(i), l, l2)) + break; + } + } + } else { + for (long i = firstFullLongIndex; i < toLongIndex; i++) { + while (true) { + long l = readVolatileLong(i); + long l2 = ~l; + if (bytes.compareAndSwapLong(firstByte(i), l, l2)) + break; + } + } + + long toByteIndex = firstByte(toLongIndex); + long mask = lowerBitsIncludingThis(toIndex); + while (true) { + long l = bytes.readVolatileLong(toByteIndex); + long l2 = l ^ mask; + if (bytes.compareAndSwapLong(toByteIndex, l, l2)) + return this; + } + } + } else { + long byteIndex = firstByte(fromLongIndex); + long mask = higherBitsIncludingThis(fromIndex) & lowerBitsIncludingThis(toIndex); + while (true) { + long l = bytes.readVolatileLong(byteIndex); + long l2 = l ^ mask; + if (bytes.compareAndSwapLong(byteIndex, l, l2)) + return this; + } + } + return this; + } + + @Override + public DirectBitSet set(long bitIndex) { + long longIndex = longWithThisBit(bitIndex); + if (bitIndex < 0 || longIndex >= longLength) + throw new IndexOutOfBoundsException(); + long byteIndex = firstByte(longIndex); + long mask = singleBit(bitIndex); + while (true) { + long l = bytes.readVolatileLong(byteIndex); + if ((l & mask) != 0) return this; + long l2 = l | mask; + if (bytes.compareAndSwapLong(byteIndex, l, l2)) + return this; + } + } + + @Override + public boolean setIfClear(long bitIndex) { + long longIndex = longWithThisBit(bitIndex); + if (bitIndex < 0 || longIndex >= longLength) + throw new IndexOutOfBoundsException(); + long byteIndex = firstByte(longIndex); + long mask = singleBit(bitIndex); + while (true) { + long l = bytes.readVolatileLong(byteIndex); + long l2 = l | mask; + if (l == l2) + return false; + if (bytes.compareAndSwapLong(byteIndex, l, l2)) + return true; + } + } + + @Override + public DirectBitSet set(long bitIndex, boolean value) { + return value ? set(bitIndex) : clear(bitIndex); + } + + @Override + public DirectBitSet set(long fromIndex, long exclusiveToIndex) { + long fromLongIndex = longWithThisBit(fromIndex); + long toIndex = exclusiveToIndex - 1; + long toLongIndex = longWithThisBit(toIndex); + if (fromIndex < 0 || fromIndex > exclusiveToIndex || + toLongIndex >= longLength) + throw new IndexOutOfBoundsException(); + + if (fromLongIndex != toLongIndex) { + long firstFullLongIndex = fromLongIndex; + if ((fromIndex & 63) != 0) { + long fromByteIndex = firstByte(fromLongIndex); + long mask = higherBitsIncludingThis(fromIndex); + while (true) { + long l = bytes.readVolatileLong(fromByteIndex); + long l2 = l | mask; + if (bytes.compareAndSwapLong(fromByteIndex, l, l2)) + break; + } + firstFullLongIndex++; + } + + if ((exclusiveToIndex & 63) == 0) { + for (long i = firstFullLongIndex; i <= toLongIndex; i++) { + bytes.writeLong(firstByte(i), ALL_ONES); + } + } else { + for (long i = firstFullLongIndex; i < toLongIndex; i++) { + bytes.writeLong(firstByte(i), ALL_ONES); + } + + long toByteIndex = firstByte(toLongIndex); + long mask = lowerBitsIncludingThis(toIndex); + while (true) { + long l = bytes.readVolatileLong(toByteIndex); + long l2 = l | mask; + if (bytes.compareAndSwapLong(toByteIndex, l, l2)) + return this; + } + } + } else { + long byteIndex = firstByte(fromLongIndex); + long mask = higherBitsIncludingThis(fromIndex) & lowerBitsIncludingThis(toIndex); + while (true) { + long l = bytes.readVolatileLong(byteIndex); + long l2 = l | mask; + if (bytes.compareAndSwapLong(byteIndex, l, l2)) + return this; + } + } + return this; + } + + @Override + public DirectBitSet setAll() { + for (long i = 0; i < longLength; i++) { + bytes.writeLong(firstByte(i), ALL_ONES); + } + return this; + } + + @Override + public DirectBitSet set(long fromIndex, long toIndex, boolean value) { + return value ? set(fromIndex, toIndex) : clear(fromIndex, toIndex); + } + + @Override + public DirectBitSet clear(long bitIndex) { + long longIndex = longWithThisBit(bitIndex); + if (bitIndex < 0 || longIndex >= longLength) + throw new IndexOutOfBoundsException(); + long byteIndex = firstByte(longIndex); + long mask = singleBit(bitIndex); + while (true) { + long l = bytes.readVolatileLong(byteIndex); + if ((l & mask) == 0) return this; + long l2 = l & ~mask; + if (bytes.compareAndSwapLong(byteIndex, l, l2)) + return this; + } + } + + @Override + public boolean clearIfSet(long bitIndex) { + long longIndex = longWithThisBit(bitIndex); + if (bitIndex < 0 || longIndex >= longLength) + throw new IndexOutOfBoundsException(); + long byteIndex = firstByte(longIndex); + long mask = singleBit(bitIndex); + while (true) { + long l = bytes.readVolatileLong(byteIndex); + if ((l & mask) == 0) return false; + long l2 = l & ~mask; + if (bytes.compareAndSwapLong(byteIndex, l, l2)) + return true; + } + } + + @Override + public DirectBitSet clear(long fromIndex, long exclusiveToIndex) { + long fromLongIndex = longWithThisBit(fromIndex); + long toIndex = exclusiveToIndex - 1; + long toLongIndex = longWithThisBit(toIndex); + if (fromIndex < 0 || fromIndex > exclusiveToIndex || + toLongIndex >= longLength) + throw new IndexOutOfBoundsException(); + + if (fromLongIndex != toLongIndex) { + long firstFullLongIndex = fromLongIndex; + if ((fromIndex & 63) != 0) { + long fromByteIndex = firstByte(fromLongIndex); + long mask = lowerBitsExcludingThis(fromIndex); + while (true) { + long l = bytes.readVolatileLong(fromByteIndex); + long l2 = l & mask; + if (bytes.compareAndSwapLong(fromByteIndex, l, l2)) + break; + } + firstFullLongIndex++; + } + + if ((exclusiveToIndex & 63) == 0) { + for (long i = firstFullLongIndex; i <= toLongIndex; i++) { + bytes.writeLong(firstByte(i), 0L); + } + } else { + for (long i = firstFullLongIndex; i < toLongIndex; i++) { + bytes.writeLong(firstByte(i), 0L); + } + + long toByteIndex = firstByte(toLongIndex); + long mask = higherBitsExcludingThis(toIndex); + while (true) { + long l = bytes.readVolatileLong(toByteIndex); + long l2 = l & mask; + if (bytes.compareAndSwapLong(toByteIndex, l, l2)) + return this; + } + } + } else { + long byteIndex = firstByte(fromLongIndex); + long mask = lowerBitsExcludingThis(fromIndex) | (higherBitsExcludingThis(toIndex)); + while (true) { + long l = bytes.readVolatileLong(byteIndex); + long l2 = l & mask; + if (bytes.compareAndSwapLong(byteIndex, l, l2)) + return this; + } + } + return this; + } + + @Override + public DirectBitSet clear() { + bytes.zeroOut(); + return this; + } + + @Override + public boolean get(long bitIndex) { + long longIndex = longWithThisBit(bitIndex); + if (bitIndex < 0 || longIndex >= longLength) + throw new IndexOutOfBoundsException(); + long l = readVolatileLong(longIndex); + return (l & (singleBit(bitIndex))) != 0; + } + + @Override + public boolean isSet(long bitIndex) { + return get(bitIndex); + } + + @Override + public boolean isClear(long bitIndex) { + return !get(bitIndex); + } + + @Override + public long getLong(long longIndex) { + if (longIndex < 0 || longIndex >= longLength) + throw new IndexOutOfBoundsException(); + return readVolatileLong(longIndex); + } + + @Override + public long nextSetBit(long fromIndex) { + return bytes.nextSetBit(fromIndex); + } + + @Override + public Bits setBits() { + return new SetBits(); + } + + @Override + public long clearNextSetBit(long fromIndex) { + if (fromIndex < 0) + throw new IndexOutOfBoundsException(); + long fromLongIndex = longWithThisBit(fromIndex); + if (fromLongIndex >= longLength) + return NOT_FOUND; + long fromByteIndex = firstByte(fromLongIndex); + while (true) { + long w = bytes.readVolatileLong(fromByteIndex); + long l = w >>> fromIndex; + if (l != 0) { + long indexOfSetBit = fromIndex + numberOfTrailingZeros(l); + long mask = singleBit(indexOfSetBit); + if (bytes.compareAndSwapLong(fromByteIndex, w, w ^ mask)) + return indexOfSetBit; + + } else { + break; + } + } + longLoop: + for (long i = fromLongIndex + 1; i < longLength; i++) { + long byteIndex = firstByte(i); + while (true) { + long l = bytes.readLong(byteIndex); + if (l != 0) { + long indexOfSetBit = firstBit(i) + numberOfTrailingZeros(l); + long mask = singleBit(indexOfSetBit); + if (bytes.compareAndSwapLong(byteIndex, l, l ^ mask)) + return indexOfSetBit; + + } else { + continue longLoop; + } + } + } + return NOT_FOUND; + } + + @Override + public long nextSetLong(long fromLongIndex) { + if (fromLongIndex < 0) + throw new IndexOutOfBoundsException(); + if (fromLongIndex >= longLength) + return NOT_FOUND; + if (readVolatileLong(fromLongIndex) != 0) + return fromLongIndex; + for (long i = fromLongIndex + 1; i < longLength; i++) { + if (readLong(i) != 0) + return i; + } + return NOT_FOUND; + } + + @Override + public long nextClearBit(long fromIndex) { + if (fromIndex < 0) + throw new IndexOutOfBoundsException(); + long fromLongIndex = longWithThisBit(fromIndex); + if (fromLongIndex >= longLength) + return NOT_FOUND; + long l = (~readVolatileLong(fromLongIndex)) >>> fromIndex; + if (l != 0) { + return fromIndex + numberOfTrailingZeros(l); + } + for (long i = fromLongIndex + 1; i < longLength; i++) { + l = ~readLong(i); + if (l != 0) + return firstBit(i) + numberOfTrailingZeros(l); + } + return NOT_FOUND; + } + + @Override + public long setNextClearBit(long fromIndex) { + if (fromIndex < 0) + throw new IndexOutOfBoundsException(); + long fromLongIndex = longWithThisBit(fromIndex); + if (fromLongIndex >= longLength) + return NOT_FOUND; + long fromByteIndex = firstByte(fromLongIndex); + while (true) { + long w = bytes.readVolatileLong(fromByteIndex); + long l = (~w) >>> fromIndex; + if (l != 0) { + long indexOfClearBit = + fromIndex + numberOfTrailingZeros(l); + long mask = singleBit(indexOfClearBit); + if (bytes.compareAndSwapLong(fromByteIndex, w, w ^ mask)) + return indexOfClearBit; + + } else { + break; + } + } + longLoop: + for (long i = fromLongIndex + 1; i < longLength; i++) { + long byteIndex = firstByte(i); + while (true) { + long w = bytes.readLong(byteIndex); + long l = ~w; + if (l != 0) { + long indexOfClearBit = firstBit(i) + numberOfTrailingZeros(l); + long mask = singleBit(indexOfClearBit); + if (bytes.compareAndSwapLong(byteIndex, w, w ^ mask)) + return indexOfClearBit; + + } else { + continue longLoop; + } + } + } + return NOT_FOUND; + } + + @Override + public long nextClearLong(long fromLongIndex) { + if (fromLongIndex < 0) + throw new IndexOutOfBoundsException(); + if (fromLongIndex >= longLength) + return NOT_FOUND; + if (readVolatileLong(fromLongIndex) != ALL_ONES) + return fromLongIndex; + for (long i = fromLongIndex + 1; i < longLength; i++) { + if (readLong(i) != ALL_ONES) + return i; + } + return NOT_FOUND; + } + + @Override + public long previousSetBit(long fromIndex) { + if (checkNotFoundIndex(fromIndex)) + return NOT_FOUND; + long fromLongIndex = longWithThisBit(fromIndex); + if (fromLongIndex >= longLength) { + // the same policy for this "index out of bounds" situation + // as in j.u.BitSet + fromLongIndex = longLength - 1; + fromIndex = size() - 1; + } + // << ~fromIndex === << (63 - (fromIndex & 63)) + long l = readVolatileLong(fromLongIndex) << ~fromIndex; + if (l != 0) + return fromIndex - numberOfLeadingZeros(l); + for (long i = fromLongIndex - 1; i >= 0; i--) { + l = readLong(i); + if (l != 0) + return lastBit(i) - numberOfLeadingZeros(l); + } + return NOT_FOUND; + } + + @Override + public long clearPreviousSetBit(long fromIndex) { + if (checkNotFoundIndex(fromIndex)) + return NOT_FOUND; + long fromLongIndex = longWithThisBit(fromIndex); + if (fromLongIndex >= longLength) { + fromLongIndex = longLength - 1; + fromIndex = size() - 1; + } + long fromByteIndex = firstByte(fromLongIndex); + while (true) { + long w = bytes.readVolatileLong(fromByteIndex); + long l = w << ~fromIndex; + if (l != 0) { + long indexOfSetBit = fromIndex - numberOfLeadingZeros(l); + long mask = singleBit(indexOfSetBit); + if (bytes.compareAndSwapLong(fromByteIndex, w, w ^ mask)) + return indexOfSetBit; + + } else { + break; + } + } + longLoop: + for (long i = fromLongIndex - 1; i >= 0; i--) { + long byteIndex = firstByte(i); + while (true) { + long l = bytes.readLong(byteIndex); + if (l != 0) { + long indexOfSetBit = lastBit(i) - numberOfLeadingZeros(l); + long mask = singleBit(indexOfSetBit); + if (bytes.compareAndSwapLong(byteIndex, l, l ^ mask)) + return indexOfSetBit; + + } else { + continue longLoop; + } + } + } + return NOT_FOUND; + } + + @Override + public long previousSetLong(long fromLongIndex) { + if (checkNotFoundIndex(fromLongIndex)) + return NOT_FOUND; + if (fromLongIndex >= longLength) + fromLongIndex = longLength - 1; + if (readVolatileLong(fromLongIndex) != 0) + return fromLongIndex; + for (long i = fromLongIndex - 1; i >= 0; i--) { + if (readLong(i) != 0) + return i; + } + return NOT_FOUND; + } + + @Override + public long previousClearBit(long fromIndex) { + if (checkNotFoundIndex(fromIndex)) + return NOT_FOUND; + long fromLongIndex = longWithThisBit(fromIndex); + if (fromLongIndex >= longLength) { + fromLongIndex = longLength - 1; + fromIndex = size() - 1; + } + long l = (~readVolatileLong(fromLongIndex)) << ~fromIndex; + if (l != 0) + return fromIndex - numberOfLeadingZeros(l); + for (long i = fromLongIndex - 1; i >= 0; i--) { + l = ~readLong(i); + if (l != 0) + return lastBit(i) - numberOfLeadingZeros(l); + } + return NOT_FOUND; + } + + @Override + public long setPreviousClearBit(long fromIndex) { + if (checkNotFoundIndex(fromIndex)) + return NOT_FOUND; + long fromLongIndex = longWithThisBit(fromIndex); + if (fromLongIndex >= longLength) { + fromLongIndex = longLength - 1; + fromIndex = size() - 1; + } + long fromByteIndex = firstByte(fromLongIndex); + while (true) { + long w = bytes.readVolatileLong(fromByteIndex); + long l = (~w) << ~fromIndex; + if (l != 0) { + long indexOfClearBit = fromIndex - numberOfLeadingZeros(l); + long mask = singleBit(indexOfClearBit); + if (bytes.compareAndSwapLong(fromByteIndex, w, w ^ mask)) + return indexOfClearBit; + + } else { + break; + } + } + longLoop: + for (long i = fromLongIndex - 1; i >= 0; i--) { + long byteIndex = firstByte(i); + while (true) { + long w = bytes.readLong(byteIndex); + long l = ~w; + if (l != 0) { + long indexOfClearBit = lastBit(i) - numberOfLeadingZeros(l); + long mask = singleBit(indexOfClearBit); + if (bytes.compareAndSwapLong(byteIndex, w, w ^ mask)) + return indexOfClearBit; + + } else { + continue longLoop; + } + } + } + return NOT_FOUND; + } + + @Override + public long previousClearLong(long fromLongIndex) { + if (checkNotFoundIndex(fromLongIndex)) + return NOT_FOUND; + if (fromLongIndex >= longLength) + fromLongIndex = longLength - 1; + if (readVolatileLong(fromLongIndex) != ALL_ONES) + return fromLongIndex; + for (long i = fromLongIndex - 1; i >= 0; i--) { + if (readLong(i) != ALL_ONES) + return i; + } + return NOT_FOUND; + } + + @Override + public long size() { + return longLength << 6; + } + + @Override + public long cardinality() { + long count = Long.bitCount(bytes.readVolatileLong(0)); + for (long i = 1; i < longLength; i++) { + count += Long.bitCount(readLong(i)); + } + return count; + } + + @Override + public DirectBitSet and(long longIndex, long value) { + while (true) { + long l = readVolatileLong(longIndex); + long l2 = l & value; + if (l == l2 || bytes.compareAndSwapLong(firstByte(longIndex), l, l2)) return this; + } + } + + @Override + public DirectBitSet or(long longIndex, long value) { + while (true) { + long l = readVolatileLong(longIndex); + long l2 = l | value; + if (l == l2 || bytes.compareAndSwapLong(firstByte(longIndex), l, l2)) return this; + } + } + + @Override + public DirectBitSet xor(long longIndex, long value) { + while (true) { + long l = readVolatileLong(longIndex); + long l2 = l ^ value; + if (bytes.compareAndSwapLong(firstByte(longIndex), l, l2)) return this; + } + } + + @Override + public DirectBitSet andNot(long longIndex, long value) { + while (true) { + long l = readVolatileLong(longIndex); + long l2 = l & ~value; + if (bytes.compareAndSwapLong(firstByte(longIndex), l, l2)) return this; + } + } + + /** + * WARNING! This implementation doesn't strictly follow the contract + * from {@code DirectBitSet} interface. For the sake of atomicity this + * implementation couldn't find and flip the range crossing native word + * boundary, e. g. bits from 55 to 75 (boundary is 64). + * + * @throws IllegalArgumentException if {@code numberOfBits} + * is out of range {@code 0 < numberOfBits && numberOfBits <= 64} + */ + @Override + public long setNextNContinuousClearBits(long fromIndex, int numberOfBits) { + checkNumberOfBits(numberOfBits); + if (numberOfBits == 1) + return setNextClearBit(fromIndex); + if (fromIndex < 0) + throw new IndexOutOfBoundsException(); + + int n64Complement = 64 - numberOfBits; + long nTrailingOnes = ALL_ONES >>> n64Complement; + + long bitIndex = fromIndex; + long longIndex = longWithThisBit(bitIndex); + long byteIndex = firstByte(longIndex); + long w, l; + if ((bitIndex & 63) > n64Complement) { + if (++longIndex >= longLength) + return NOT_FOUND; + byteIndex += 8; + bitIndex = firstBit(longIndex); + l = w = bytes.readVolatileLong(byteIndex); + + } else { + if (longIndex >= longLength) + return NOT_FOUND; + w = bytes.readVolatileLong(byteIndex); + l = rightShiftOneFill(w, bitIndex); + } + // long loop + while (true) { + continueLongLoop: + { + // (1) + if ((l & 1) != 0) { + long x = ~l; + if (x != 0) { + int trailingOnes = numberOfTrailingZeros(x); + bitIndex += trailingOnes; + // i. e. bitIndex + numberOfBits crosses 64 boundary + if ((bitIndex & 63) > n64Complement) + break continueLongLoop; + // (2) + l = rightShiftOneFill(l, trailingOnes); + + } else { + // all bits are ones, go to the next long + break continueLongLoop; + } + } + // bit search within a long + while (true) { + // CAS retry loop + while ((l & nTrailingOnes) == 0) { + long mask = nTrailingOnes << bitIndex; + if (bytes.compareAndSwapLong(byteIndex, w, w ^ mask)) { + return bitIndex; + + } else { + w = bytes.readLong(byteIndex); + l = rightShiftOneFill(w, bitIndex); + } + } + // n > trailing zeros > 0 + // > 0 ensured by block (1) + int trailingZeros = numberOfTrailingZeros(l); + bitIndex += trailingZeros; + // (3) + l = rightShiftOneFill(l, trailingZeros); + + long x = ~l; + if (x != 0) { + int trailingOnes = numberOfTrailingZeros(x); + bitIndex += trailingOnes; + // i. e. bitIndex + numberOfBits crosses 64 boundary + if ((bitIndex & 63) > n64Complement) + break continueLongLoop; + // already shifted with one-filling at least once + // at (2) or (3), => garanteed highest bit is 1 => + // "natural" one-filling + l >>= trailingOnes; + + } else { + // zeros in this long exhausted, go to the next long + break continueLongLoop; + } + } + } + if (++longIndex >= longLength) + return NOT_FOUND; + byteIndex += 8; + bitIndex = firstBit(longIndex); + l = w = bytes.readLong(byteIndex); + } + } + + /** + * WARNING! This implementation doesn't strictly follow the contract + * from {@code DirectBitSet} interface. For the sake of atomicity this + * implementation couldn't find and flip the range crossing native word + * boundary, e. g. bits from 55 to 75 (boundary is 64). + * + * @throws IllegalArgumentException if {@code numberOfBits} + * is out of range {@code 0 < numberOfBits && numberOfBits <= 64} + */ + @Override + public long clearNextNContinuousSetBits(long fromIndex, int numberOfBits) { + checkNumberOfBits(numberOfBits); + if (numberOfBits == 1) + return clearNextSetBit(fromIndex); + if (fromIndex < 0) + throw new IndexOutOfBoundsException(); + + int n64Complement = 64 - numberOfBits; + long nTrailingOnes = ALL_ONES >>> n64Complement; + + long bitIndex = fromIndex; + long longIndex = longWithThisBit(bitIndex); + long byteIndex = firstByte(longIndex); + long w, l; + if ((bitIndex & 63) > n64Complement) { + if (++longIndex >= longLength) + return NOT_FOUND; + byteIndex += 8; + bitIndex = firstBit(longIndex); + l = w = bytes.readVolatileLong(byteIndex); + + } else { + if (longIndex >= longLength) + return NOT_FOUND; + w = bytes.readVolatileLong(byteIndex); + l = w >>> bitIndex; + } + // long loop + while (true) { + continueLongLoop: + { + if ((l & 1) == 0) { + if (l != 0) { + int trailingZeros = numberOfTrailingZeros(l); + bitIndex += trailingZeros; + // i. e. bitIndex + numberOfBits crosses 64 boundary + if ((bitIndex & 63) > n64Complement) + break continueLongLoop; + l >>>= trailingZeros; + + } else { + // all bits are zeros, go to the next long + break continueLongLoop; + } + } + // bit search within a long + while (true) { + // CAS retry loop + while (((~l) & nTrailingOnes) == 0) { + long mask = nTrailingOnes << bitIndex; + if (bytes.compareAndSwapLong(byteIndex, w, w ^ mask)) { + return bitIndex; + + } else { + w = bytes.readLong(byteIndex); + l = w >>> bitIndex; + } + } + // n > trailing ones > 0 + int trailingOnes = numberOfTrailingZeros(~l); + bitIndex += trailingOnes; + l >>>= trailingOnes; + + if (l != 0) { + int trailingZeros = numberOfTrailingZeros(l); + bitIndex += trailingZeros; + // i. e. bitIndex + numberOfBits crosses 64 boundary + if ((bitIndex & 63) > n64Complement) + break continueLongLoop; + l >>>= trailingZeros; + + } else { + // ones in this long exhausted, go to the next long + break continueLongLoop; + } + } + } + if (++longIndex >= longLength) + return NOT_FOUND; + byteIndex += 8; + bitIndex = firstBit(longIndex); + l = w = bytes.readLong(byteIndex); + } + } + + /** + * WARNING! This implementation doesn't strictly follow the contract + * from {@code DirectBitSet} interface. For the sake of atomicity this + * implementation couldn't find and flip the range crossing native word + * boundary, e. g. bits from 55 to 75 (boundary is 64). + * + * @throws IllegalArgumentException if {@code numberOfBits} + * is out of range {@code 0 < numberOfBits && numberOfBits <= 64} + */ + @Override + public long setPreviousNContinuousClearBits( + long fromIndex, int numberOfBits) { + checkNumberOfBits(numberOfBits); + if (numberOfBits == 1) + return setPreviousClearBit(fromIndex); + if (checkNotFoundIndex(fromIndex)) + return NOT_FOUND; + + int numberOfBitsMinusOne = numberOfBits - 1; + long nLeadingOnes = ALL_ONES << (64 - numberOfBits); + + long bitIndex = fromIndex; + long longIndex = longWithThisBit(bitIndex); + if (longIndex >= longLength) { + longIndex = longLength - 1; + bitIndex = lastBit(longIndex); + } + long byteIndex = firstByte(longIndex); + long w, l; + if ((bitIndex & 63) < numberOfBitsMinusOne) { + if (--longIndex < 0) + return NOT_FOUND; + byteIndex -= 8; + bitIndex = lastBit(longIndex); + l = w = bytes.readVolatileLong(byteIndex); + + } else { + w = bytes.readVolatileLong(byteIndex); + // left shift by ~bitIndex === left shift by (63 - (bitIndex & 63)) + l = leftShiftOneFill(w, ~bitIndex); + } + // long loop + while (true) { + continueLongLoop: + { + if (l < 0) { // condition means the highest bit is one + long x = ~l; + if (x != 0) { + int leadingOnes = numberOfLeadingZeros(x); + bitIndex -= leadingOnes; + if ((bitIndex & 63) < numberOfBitsMinusOne) + break continueLongLoop; + l = leftShiftOneFill(l, leadingOnes); + + } else { + // all bits are ones, go to the next long + break continueLongLoop; + } + } + // bit search within a long + while (true) { + // CAS retry loop + while ((l & nLeadingOnes) == 0) { + // >>> ~bitIndex === >>> (63 - (butIndex & 63)) + long mask = nLeadingOnes >>> ~bitIndex; + if (bytes.compareAndSwapLong(byteIndex, w, w ^ mask)) { + return bitIndex - numberOfBitsMinusOne; + + } else { + w = bytes.readLong(byteIndex); + l = leftShiftOneFill(w, ~bitIndex); + } + } + // n > leading zeros > 0 + int leadingZeros = numberOfLeadingZeros(l); + bitIndex -= leadingZeros; + l = leftShiftOneFill(l, leadingZeros); + + long x = ~l; + if (x != 0) { + int leadingOnes = numberOfLeadingZeros(x); + bitIndex -= leadingOnes; + if ((bitIndex & 63) < numberOfBitsMinusOne) + break continueLongLoop; + l = leftShiftOneFill(l, leadingOnes); + + } else { + // zeros in this long exhausted, go to the next long + break continueLongLoop; + } + } + } + if (--longIndex < 0) + return NOT_FOUND; + byteIndex -= 8; + bitIndex = lastBit(longIndex); + l = w = bytes.readLong(byteIndex); + } + } + + /** + * WARNING! This implementation doesn't strictly follow the contract + * from {@code DirectBitSet} interface. For the sake of atomicity this + * implementation couldn't find and flip the range crossing native word + * boundary, e. g. bits from 55 to 75 (boundary is 64). + * + * @throws IllegalArgumentException if {@code numberOfBits} + * is out of range {@code 0 < numberOfBits && numberOfBits <= 64} + */ + @Override + public long clearPreviousNContinuousSetBits( + long fromIndex, int numberOfBits) { + checkNumberOfBits(numberOfBits); + if (numberOfBits == 1) + return clearPreviousSetBit(fromIndex); + if (checkNotFoundIndex(fromIndex)) + return NOT_FOUND; + + int numberOfBitsMinusOne = numberOfBits - 1; + long nLeadingOnes = ALL_ONES << (64 - numberOfBits); + + long bitIndex = fromIndex; + long longIndex = longWithThisBit(bitIndex); + if (longIndex >= longLength) { + longIndex = longLength - 1; + bitIndex = lastBit(longIndex); + } + long byteIndex = firstByte(longIndex); + long w, l; + if ((bitIndex & 63) < numberOfBitsMinusOne) { + if (--longIndex < 0) + return NOT_FOUND; + byteIndex -= 8; + bitIndex = lastBit(longIndex); + l = w = bytes.readVolatileLong(byteIndex); + + } else { + w = bytes.readVolatileLong(byteIndex); + // << ~bitIndex === << (63 - (bitIndex & 63)) + l = w << ~bitIndex; + } + // long loop + while (true) { + continueLongLoop: + { + // condition means the highest bit is zero, but not all + if (l > 0) { + int leadingZeros = numberOfLeadingZeros(l); + bitIndex -= leadingZeros; + if ((bitIndex & 63) < numberOfBitsMinusOne) + break continueLongLoop; + l <<= leadingZeros; + + } else if (l == 0) { + // all bits are zeros, go to the next long + break continueLongLoop; + } + // bit search within a long + while (true) { + // CAS retry loop + while (((~l) & nLeadingOnes) == 0) { + // >>> ~bitIndex === >>> (63 - (butIndex & 63)) + long mask = nLeadingOnes >>> ~bitIndex; + if (bytes.compareAndSwapLong(byteIndex, w, w ^ mask)) { + return bitIndex - numberOfBitsMinusOne; + + } else { + w = bytes.readLong(byteIndex); + l = w << ~bitIndex; + } + } + // n > leading ones > 0 + int leadingOnes = numberOfLeadingZeros(~l); + bitIndex -= leadingOnes; + l <<= leadingOnes; + + if (l != 0) { + int leadingZeros = numberOfLeadingZeros(l); + bitIndex -= leadingZeros; + if ((bitIndex & 63) < numberOfBitsMinusOne) + break continueLongLoop; + l <<= leadingZeros; + + } else { + // ones in this long exhausted, go to the next long + break continueLongLoop; + } + } + } + if (--longIndex < 0) + return NOT_FOUND; + byteIndex -= 8; + bitIndex = lastBit(longIndex); + l = w = bytes.readLong(byteIndex); + } + } + + private class SetBits implements Bits { + private final long byteLength = longLength << 3; + private long byteIndex = 0; + private long bitIndex = 0; + + @Override + public long next() { + long bitIndex = this.bitIndex; + if (bitIndex >= 0) { + long i = byteIndex; + long l = bytes.readVolatileLong(i) >>> bitIndex; + if (l != 0) { + int trailingZeros = numberOfTrailingZeros(l); + long index = bitIndex + trailingZeros; + if (((this.bitIndex = index + 1) & 63) == 0) { + if ((byteIndex = i + 8) == byteLength) + this.bitIndex = -1; + } + return index; + } + for (long lim = byteLength; (i += 8) < lim; ) { + if ((l = bytes.readLong(i)) != 0) { + int trailingZeros = numberOfTrailingZeros(l); + long index = (i << 3) + trailingZeros; + if (((this.bitIndex = index + 1) & 63) != 0) { + byteIndex = i; + + } else { + if ((byteIndex = i + 8) == lim) + this.bitIndex = -1; + } + return index; + } + } + } + this.bitIndex = -1; + return -1; + } + } +} diff --git a/lang/src/main/java/net/openhft/lang/collection/DirectBitSet.java b/lang/src/main/java/net/openhft/lang/collection/DirectBitSet.java new file mode 100644 index 0000000..2416075 --- /dev/null +++ b/lang/src/main/java/net/openhft/lang/collection/DirectBitSet.java @@ -0,0 +1,557 @@ +/* + * Copyright (C) 2015 higherfrequencytrading.com + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as published by + * the Free Software Foundation, either version 3 of the License. + * + * 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 Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with this program. If not, see <http://www.gnu.org/licenses/>. + */ + +package net.openhft.lang.collection; + +import net.openhft.lang.ReferenceCounted; + +/** + * Most notable logical difference of this interface with {@code java.util.BitSet} + * is that {@code DirectBitSet} has a rigid {@link #size}, + * attempts to {@link #get}, {@link #set} or {@link #clear} bits at indices + * exceeding the size cause {@code IndexOutOfBoundsException}. There is also + * a {@link #setAll()} method to set all bits within the size. + * {@code java.util.BitSet} doesn't have such rigid capacity. + * + * @see java.util.BitSet + */ +public interface DirectBitSet extends ReferenceCounted { + /** + * Returned if no entry is found + */ + long NOT_FOUND = -1L; + + /** + * Sets the bit at the specified index to the complement of its + * current value. + * + * @param bitIndex the index of the bit to flip + * @return this {@code DirectBitSet} back + * @throws IndexOutOfBoundsException if the index is out of range + * {@code (index < 0 || index >= size())} + */ + DirectBitSet flip(long bitIndex); + + /** + * Sets each bit from the specified {@code fromIndex} (inclusive) to the + * specified {@code toIndex} (exclusive) to the complement of its current + * value. + * + * @param fromIndex index of the first bit to flip + * @param toIndex index after the last bit to flip + * @return this {@code DirectBitSet} back + * @throws IndexOutOfBoundsException if {@code fromIndex} is negative, + * or {@code fromIndex} is larger than {@code toIndex}, + * or {@code toIndex} is larger or equal to {@code size()} + */ + DirectBitSet flip(long fromIndex, long toIndex); + + /** + * Sets the bit at the specified index to {@code true}. + * + * @param bitIndex a bit index + * @return this {@code DirectBitSet} back + * @throws IndexOutOfBoundsException if the index is out of range + * {@code (index < 0 || index >= size())} + */ + DirectBitSet set(long bitIndex); + + /** + * Sets the bit at the specified index to {@code true}. + * + * @param bitIndex a bit index + * @return true if the bit was zeroOut, or false if the bit was already set. + * @throws IndexOutOfBoundsException if the index is out of range + * {@code (index < 0 || index >= size())} + */ + boolean setIfClear(long bitIndex); + + /** + * Clears the bit at the specified index (sets it to {@code false}). + * + * @param bitIndex a bit index + * @return the previous value of the bit at the specified index + * @throws IndexOutOfBoundsException if the index is out of range + * {@code (index < 0 || index >= size())} + */ + boolean clearIfSet(long bitIndex); + + /** + * Sets the bit at the specified index to the specified value. + * + * @param bitIndex a bit index + * @param value a boolean value to set + * @return this {@code DirectBitSet} back + * @throws IndexOutOfBoundsException if the index is out of range + * {@code (index < 0 || index >= size())} + */ + DirectBitSet set(long bitIndex, boolean value); + + /** + * Sets the bits from the specified {@code fromIndex} (inclusive) to the + * specified {@code toIndex} (exclusive) to {@code true}. + * + * @param fromIndex index of the first bit to be set + * @param toIndex index after the last bit to be set + * @return this {@code DirectBitSet} back + * @throws IndexOutOfBoundsException if {@code fromIndex} is negative, + * or {@code fromIndex} is larger than {@code toIndex}, + * or {@code toIndex} is larger or equal to {@code size()} + */ + DirectBitSet set(long fromIndex, long toIndex); + + /** + * Sets all bits, {@code bs.setAll()} is equivalent + * of {@code bs.set(0, bs.size()}. + * + * @return this bit set back + */ + DirectBitSet setAll(); + + /** + * Sets the bits from the specified {@code fromIndex} (inclusive) to the + * specified {@code toIndex} (exclusive) to the specified value. + * + * @param fromIndex index of the first bit to be set + * @param toIndex index after the last bit to be set + * @param value value to set the selected bits to + * @return this {@code DirectBitSet} back + * @throws IndexOutOfBoundsException if {@code fromIndex} is negative, + * or {@code fromIndex} is larger than {@code toIndex}, + * or {@code toIndex} is larger or equal to {@code size()} + */ + DirectBitSet set(long fromIndex, long toIndex, boolean value); + + /** + * Sets the bit specified by the index to {@code false}. + * + * @param bitIndex the index of the bit to be cleared + * @return this {@code DirectBitSet} back + * @throws IndexOutOfBoundsException if the index is out of range + * {@code (index < 0 || index >= size())} + */ + DirectBitSet clear(long bitIndex); + + /** + * Sets the bits from the specified {@code fromIndex} (inclusive) to the + * specified {@code toIndex} (exclusive) to {@code false}. + * + * @param fromIndex index of the first bit to be cleared + * @param toIndex index after the last bit to be cleared + * @return this {@code DirectBitSet} back + * @throws IndexOutOfBoundsException if {@code fromIndex} is negative, + * or {@code fromIndex} is larger than {@code toIndex}, + * or {@code toIndex} is larger or equal to {@code size()} + */ + DirectBitSet clear(long fromIndex, long toIndex); + + /** + * Sets all of the bits in this BitSet to {@code false}. + * + * @return this {@code DirectBitSet} back + */ + DirectBitSet clear(); + + /** + * Returns the value of the bit with the specified index. The value + * is {@code true} if the bit with the index {@code bitIndex} + * is currently set in this {@code DirectBitSet}; otherwise, the result + * is {@code false}. + * + * @param bitIndex the bit index + * @return the value of the bit with the specified index + * @throws IndexOutOfBoundsException if the index is out of range + * {@code (index < 0 || index >= size())} + */ + boolean get(long bitIndex); + + /** + * Synonym of {@link #get(long)}. + * + * @param bitIndex the bit index + * @return the value of the bit with the specified index + * @throws IndexOutOfBoundsException if the index is out of range + * {@code (index < 0 || index >= size())} + */ + boolean isSet(long bitIndex); + + /** + * Synonym of {@code !get(long)}. + * @param bitIndex the bit index + * @return {@code true} is the bit at the specified index is clear in this + * bit set; if the bit is set to {@code true} then returns {@code false} + * @throws IndexOutOfBoundsException if the index is out of range + * {@code (index < 0 || index >= size())} + */ + boolean isClear(long bitIndex); + + /** + * Returns the value of the long with the specified long index. + * + * @param longIndex the bit index + * @return the value of the long with the specified index + * @throws IndexOutOfBoundsException if the index is out of range + * {@code (index < 0 || index >= size() / 64)} + */ + long getLong(long longIndex); + + /** + * Returns the index of the first bit that is set to {@code true} + * that occurs on or after the specified starting index. If no such + * bit exists then {@code -1} is returned. + * + * <p>To iterate over the {@code true} bits in a {@code DirectBitSet}, + * use the following loop: + * <pre> {@code + * for (int i = bs.nextSetBit(0); i >= 0; i = bs.nextSetBit(i+1)) { + * // operate on index i here + * }}</pre> + * + * @param fromIndex the index to start checking from (inclusive) + * @return the index of the next set bit, or {@code -1} if there + * is no such bit + * @throws IndexOutOfBoundsException if the specified index is negative + * @see #clearNextSetBit(long) + */ + long nextSetBit(long fromIndex); + + /** + * Returns the index of the first long that contains a bit set to {@code true} + * that occurs on or after the specified starting index. The index is the number of longs. + * If no such bit exists then {@code -1} is returned. + * + * @param fromLongIndex the index to start checking from (inclusive) + * @return the index of the next set long, or {@code -1} if there + * is no such long + * @throws IndexOutOfBoundsException if the specified index is negative + */ + long nextSetLong(long fromLongIndex); + + /** + * Returns the index of the first bit that is set to {@code false} + * that occurs on or after the specified starting index. If no such + * bit exists then {@code -1} is returned. + * + * @param fromIndex the index to start checking from (inclusive) + * @return the index of the next zeroOut bit, + * or {@code -1} if there is no such bit + * @throws IndexOutOfBoundsException if the specified index is negative + * @see #setNextClearBit(long) + */ + long nextClearBit(long fromIndex); + + /** + * Returns the index of the first long that contains a bit is set to {@code false} + * that occurs on or after the specified starting index. If no such + * bit exists then {@code -1} is returned. + * + * @param fromLongIndex the index to start checking from (inclusive) + * @return the index of the next long containing a zeroOut bit, + * or {@code -1} if there is no such long + * @throws IndexOutOfBoundsException if the specified index is negative + */ + long nextClearLong(long fromLongIndex); + + /** + * Returns the index of the nearest bit that is set to {@code true} + * that occurs on or before the specified starting index. + * If no such bit exists, or if {@code -1} is given as the + * starting index, then {@code -1} is returned. + * + * <p>To iterate over the {@code true} bits in a {@code DirectBitSet}, + * use the following loop: + * <pre> {@code + * for (int i = bs.size(); (i = bs.previousSetBit(i-1)) >= 0; ) { + * // operate on index i here + * }}</pre> + * + * @param fromIndex the index to start checking from (inclusive) + * @return the index of the previous set bit, or {@code -1} if there + * is no such bit + * @throws IndexOutOfBoundsException if the specified index is less + * than {@code -1} + * @see #clearPreviousSetBit(long) + */ + long previousSetBit(long fromIndex); + + /** + * Returns the index of the nearest long that contains a bit set to {@code true} + * that occurs on or before the specified starting index. + * If no such bit exists, or if {@code -1} is given as the + * starting index, then {@code -1} is returned. + * + * @param fromLongIndex the index to start checking from (inclusive) + * @return the index of the previous long containing a set bit, + * or {@code -1} if there is no such long + * @throws IndexOutOfBoundsException if the specified index is less + * than {@code -1} + */ + long previousSetLong(long fromLongIndex); + + /** + * Returns the index of the nearest bit that is set to {@code false} + * that occurs on or before the specified starting index. + * If no such bit exists, or if {@code -1} is given as the + * starting index, then {@code -1} is returned. + * + * @param fromIndex the index to start checking from (inclusive) + * @return the index of the previous zeroOut bit, or {@code -1} if there + * is no such bit + * @throws IndexOutOfBoundsException if the specified index is less + * than {@code -1} + * @see #setPreviousClearBit(long) + */ + long previousClearBit(long fromIndex); + + /** + * Returns the index of the nearest long that contains a bit set to {@code false} + * that occurs on or before the specified starting index. + * If no such bit exists, or if {@code -1} is given as the + * starting index, then {@code -1} is returned. + * + * @param fromLongIndex the index to start checking from (inclusive) + * @return the index of the previous long containing a zeroOut bit, + * or {@code -1} if there is no such long + * @throws IndexOutOfBoundsException if the specified index is less + * than {@code -1} + */ + long previousClearLong(long fromLongIndex); + + /** + * Returns the number of bits of space actually in use by this BitSet to represent bit values. + * The index of the last bit in the set eligible to be set or zeroOut + * is {@code size() - 1}. + * + * @return the number of bits in this bit set + */ + long size(); + + /** + * Returns the number of bits set to {@code true} in this {@code DirectBitSet}. + * + * @return the number of bits set to {@code true} in this {@code DirectBitSet} + */ + long cardinality(); + + /** + * Performs a logical <b>AND</b> of the long at the specified index in this + * bit set with the argument long value. + * + * @param longIndex of long to AND + * @param value of long to AND + * @return this {@code DirectBitSet} back + */ + DirectBitSet and(long longIndex, long value); + + /** + * Performs a logical <b>OR</b> of the long at the specified index in this + * bit set with the argument long value. + * + * @param longIndex of long to OR + * @param value of long to OR + * @return this {@code DirectBitSet} back + */ + DirectBitSet or(long longIndex, long value); + + /** + * Performs a logical <b>XOR</b> of the long at the specified index in this + * bit set with the argument long value. + * + * @param longIndex of long to XOR + * @param value of long to XOR + * @return this {@code DirectBitSet} back + */ + DirectBitSet xor(long longIndex, long value); + + /** + * Clears all of the bits in the long at the specified index in this + * {@code DirectBitSet} whose corresponding bit is set in the specified + * long value. + * + * @param longIndex of long to AND NOT + * @param value of long to AND NOT + * @return this {@code DirectBitSet} back + */ + DirectBitSet andNot(long longIndex, long value); + + /** + * Finds and sets to {@code true} the first bit that is set to {@code false} + * that occurs on or after the specified starting index. If no such + * bit exists then {@code -1} is returned. + * + * @param fromIndex the index to start checking from (inclusive) + * @return the index of the next zeroOut bit, + * or {@code -1} if there is no such bit + * @throws IndexOutOfBoundsException if the specified index is negative + * @see #nextClearBit(long) + */ + long setNextClearBit(long fromIndex); + + /** + * Finds and clears the first bit that is set to {@code true} + * that occurs on or after the specified starting index. If no such + * bit exists then {@code -1} is returned. + * + * @param fromIndex the index to start checking from (inclusive) + * @return the index of the next set bit, or {@code -1} if there + * is no such bit + * @throws IndexOutOfBoundsException if the specified index is negative + * @see #nextSetBit(long) + */ + long clearNextSetBit(long fromIndex); + + /** + * Finds and sets to {@code true} the nearest bit that is set + * to {@code false} that occurs on or before the specified starting index. + * If no such bit exists, or if {@code -1} is given as the + * starting index, then {@code -1} is returned. + * + * @param fromIndex the index to start checking from (inclusive) + * @return the index of the previous zeroOut bit, or {@code -1} if there + * is no such bit + * @throws IndexOutOfBoundsException if the specified index is less + * than {@code -1} + * @see #previousClearBit(long) + */ + long setPreviousClearBit(long fromIndex); + + /** + * Finds and clears the nearest bit that is set to {@code true} + * that occurs on or before the specified starting index. + * If no such bit exists, or if {@code -1} is given as the + * starting index, then {@code -1} is returned. + * + * @param fromIndex the index to start checking from (inclusive) + * @return the index of the previous set bit, or {@code -1} if there + * is no such bit + * @throws IndexOutOfBoundsException if the specified index is less + * than {@code -1} + * @see #previousSetBit(long) + */ + long clearPreviousSetBit(long fromIndex); + + /** + * Finds the next {@code numberOfBits} consecutive bits set to {@code false}, + * starting from the specified {@code fromIndex}. Then all bits of the found + * range are set to {@code true}. The first index of the found block + * is returned. If there is no such range of clear bits, {@code -1} + * is returned. + * + * <p>{@code fromIndex} could be the first index of the found range, thus + * {@code setNextNContinuousClearBits(i, 1)} is exact equivalent of + * {@code setNextClearBit(i)}. + * + * @param fromIndex the index to start checking from (inclusive) + * @param numberOfBits how many continuous clear bits to search and set + * @return the index of the first bit in the found range of clear bits, + * or {@code -1} if there is no such range + * @throws IndexOutOfBoundsException if {@code fromIndex} is negative + * @throws java.lang.IllegalArgumentException if {@code numberOfBits <= 0} + */ + long setNextNContinuousClearBits(long fromIndex, int numberOfBits); + + /** + * Finds the next {@code numberOfBits} consecutive bits set to {@code true}, + * starting from the specified {@code fromIndex}. Then all bits of the found + * range are set to {@code false}. The first index of the found block + * is returned. If there is no such range of {@code true} bits, {@code -1} + * is returned. + * + * <p>{@code fromIndex} could be the first index of the found range, thus + * {@code clearNextNContinuousSetBits(i, 1)} is exact equivalent of + * {@code clearNextSetBit(i)}. + * + * @param fromIndex the index to start checking from (inclusive) + * @param numberOfBits how many continuous set bits to search and clear + * @return the index of the first bit in the found range + * of {@code true} bits, or {@code -1} if there is no such range + * @throws IndexOutOfBoundsException if {@code fromIndex} is negative + * @throws java.lang.IllegalArgumentException if {@code numberOfBits <= 0} + */ + long clearNextNContinuousSetBits(long fromIndex, int numberOfBits); + + /** + * Finds the previous {@code numberOfBits} consecutive bits + * set to {@code false}, starting from the specified {@code fromIndex}. + * Then all bits of the found range are set to {@code true}. + * The first index of the found block is returned. If there is no such + * range of clear bits, or if {@code -1} is given as the starting index, + * {@code -1} is returned. + * + * <p>{@code fromIndex} could be the last index of the found range, thus + * {@code setPreviousNContinuousClearBits(i, 1)} is exact equivalent of + * {@code setPreviousClearBit(i)}. + * + * @param fromIndex the index to start checking from (inclusive) + * @param numberOfBits how many continuous clear bits to search and set + * @return the index of the first bit in the found range of clear bits, + * or {@code -1} if there is no such range + * @throws IndexOutOfBoundsException if {@code fromIndex} is less + * than {@code -1} + * @throws java.lang.IllegalArgumentException if {@code numberOfBits <= 0} + */ + long setPreviousNContinuousClearBits(long fromIndex, int numberOfBits); + + /** + * Finds the previous {@code numberOfBits} consecutive bits + * set to {@code true}, starting from the specified {@code fromIndex}. + * Then all bits of the found range are set to {@code false}. + * The first index of the found block is returned. If there is no such + * range of {@code true} bits, or if {@code -1} is given as the starting + * index, {@code -1} is returned. + * + * <p>{@code fromIndex} could be the last index of the found range, thus + * {@code clearPreviousNContinuousSetBits(i, 1)} is exact equivalent of + * {@code clearPreviousSetBit(i)}. + * + * @param fromIndex the index to start checking from (inclusive) + * @param numberOfBits how many continuous set bits to search and clear + * @return the index of the first bit in the found range + * of {@code true} bits, or {@code -1} if there is no such range + * @throws IndexOutOfBoundsException if {@code fromIndex} is less + * than {@code -1} + * @throws java.lang.IllegalArgumentException if {@code numberOfBits <= 0} + */ + long clearPreviousNContinuousSetBits(long fromIndex, int numberOfBits); + + /** + * An iteration of bits in a bit set. + * + * <p>Usage idiom: <pre>{@code + * Bits bits = bitSet.setBits(); + * for (long bit; (bit = bits.next()) >= 0;) { + * // do something with the bit + * }}</pre> + */ + interface Bits { + /** + * Returns index of the next bit in the iteration, + * or {@code -1} if there are no more bits. + * + * @return index of the next bit in the iteration, + * or {@code -1} if there are no more bits + */ + long next(); + } + + /** + * Returns an iteration of <i>set</i> bits in <i>direct</i> order + * (from 0 to the end of the bit set). + * + * @return an iteration of <i>set</i> bits in <i>direct</i> order + */ + Bits setBits(); +} diff --git a/lang/src/main/java/net/openhft/lang/collection/DirectBitSetBuilder.java b/lang/src/main/java/net/openhft/lang/collection/DirectBitSetBuilder.java new file mode 100644 index 0000000..33bdc53 --- /dev/null +++ b/lang/src/main/java/net/openhft/lang/collection/DirectBitSetBuilder.java @@ -0,0 +1,58 @@ +/* + * Copyright (C) 2015 higherfrequencytrading.com + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as published by + * the Free Software Foundation, either version 3 of the License. + * + * 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 Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with this program. If not, see <http://www.gnu.org/licenses/>. + */ + +package net.openhft.lang.collection; + +import net.openhft.lang.io.Bytes; +import net.openhft.lang.io.DirectStore; + +public class DirectBitSetBuilder { + private boolean assertions; + private boolean threadSafe; + + public DirectBitSetBuilder() { + threadSafe = true; + assertions = false; + //noinspection ConstantConditions,AssertWithSideEffects + assert assertions = true; + } + + static DirectBitSet wrap(Bytes bytes) { + return ATSDirectBitSet.wrap(bytes); + } + + public DirectBitSetBuilder assertions(boolean assertions) { + this.assertions = assertions; + return this; + } + + public boolean assertions() { + return assertions; + } + + public DirectBitSetBuilder threadSafe(boolean threadSafe) { + this.threadSafe = threadSafe; + return this; + } + + public boolean threadSafe() { + return threadSafe; + } + + public DirectBitSet create(long size) { + return wrap(DirectStore.allocate((size + 7) >>> 3).bytes()); + } +} diff --git a/lang/src/main/java/net/openhft/lang/collection/HugeArray.java b/lang/src/main/java/net/openhft/lang/collection/HugeArray.java index cfe1ffb..12c5962 100644 --- a/lang/src/main/java/net/openhft/lang/collection/HugeArray.java +++ b/lang/src/main/java/net/openhft/lang/collection/HugeArray.java @@ -1,17 +1,17 @@ /* - * Copyright 2013 Peter Lawrey + * Copyright (C) 2015 higherfrequencytrading.com * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as published by + * the Free Software Foundation, either version 3 of the License. * - * http://www.apache.org/licenses/LICENSE-2.0 + * 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 Lesser General Public License for more details. * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. + * You should have received a copy of the GNU Lesser General Public License + * along with this program. If not, see <http://www.gnu.org/licenses/>. */ package net.openhft.lang.collection; @@ -36,7 +36,7 @@ public interface HugeArray<T> { T get(long index); /** - * Get a copy of the object in the array + * If the element was returned by get(long), re-index it otherwise, get a copy of the object in the array. * * @param index of element to copy * @param element Copyable element to copy to. diff --git a/lang/src/main/java/net/openhft/lang/collection/HugeCollections.java b/lang/src/main/java/net/openhft/lang/collection/HugeCollections.java index 4dbaad2..72a937f 100644 --- a/lang/src/main/java/net/openhft/lang/collection/HugeCollections.java +++ b/lang/src/main/java/net/openhft/lang/collection/HugeCollections.java @@ -1,24 +1,23 @@ /* - * Copyright 2013 Peter Lawrey + * Copyright (C) 2015 higherfrequencytrading.com * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as published by + * the Free Software Foundation, either version 3 of the License. * - * http://www.apache.org/licenses/LICENSE-2.0 + * 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 Lesser General Public License for more details. * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. + * You should have received a copy of the GNU Lesser General Public License + * along with this program. If not, see <http://www.gnu.org/licenses/>. */ package net.openhft.lang.collection; import net.openhft.lang.collection.impl.HugeArrayImpl; import net.openhft.lang.collection.impl.HugeQueueImpl; -import net.openhft.lang.model.DataValueGenerator; /** * User: peter.lawrey @@ -28,21 +27,11 @@ import net.openhft.lang.model.DataValueGenerator; public enum HugeCollections { ; - private static final DataValueGenerator VALUE_GENERATOR = new DataValueGenerator(); - - static { - VALUE_GENERATOR.setDumpCode(true); - } - - public static DataValueGenerator getDataValueGenerator() { - return VALUE_GENERATOR; - } - public static <T> HugeArray<T> newArray(Class<T> tClass, long length) { - return new HugeArrayImpl<T>(VALUE_GENERATOR, tClass, length); + return new HugeArrayImpl<T>(tClass, length); } public static <T> HugeQueue<T> newQueue(Class<T> tClass, long length) { - return new HugeQueueImpl<T>(new HugeArrayImpl<T>(VALUE_GENERATOR, tClass, length + 1), length + 1); + return new HugeQueueImpl<T>(new HugeArrayImpl<T>(tClass, length + 1), length + 1); } } diff --git a/lang/src/main/java/net/openhft/lang/collection/HugeQueue.java b/lang/src/main/java/net/openhft/lang/collection/HugeQueue.java index c164fc4..4d6884b 100644 --- a/lang/src/main/java/net/openhft/lang/collection/HugeQueue.java +++ b/lang/src/main/java/net/openhft/lang/collection/HugeQueue.java @@ -1,17 +1,17 @@ /* - * Copyright 2013 Peter Lawrey + * Copyright (C) 2015 higherfrequencytrading.com * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as published by + * the Free Software Foundation, either version 3 of the License. * - * http://www.apache.org/licenses/LICENSE-2.0 + * 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 Lesser General Public License for more details. * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. + * You should have received a copy of the GNU Lesser General Public License + * along with this program. If not, see <http://www.gnu.org/licenses/>. */ package net.openhft.lang.collection; diff --git a/lang/src/main/java/net/openhft/lang/collection/SingleThreadedDirectBitSet.java b/lang/src/main/java/net/openhft/lang/collection/SingleThreadedDirectBitSet.java new file mode 100644 index 0000000..4a53765 --- /dev/null +++ b/lang/src/main/java/net/openhft/lang/collection/SingleThreadedDirectBitSet.java @@ -0,0 +1,1337 @@ +/* + * Copyright (C) 2015 higherfrequencytrading.com + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as published by + * the Free Software Foundation, either version 3 of the License. + * + * 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 Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with this program. If not, see <http://www.gnu.org/licenses/>. + */ + +package net.openhft.lang.collection; + +import net.openhft.lang.io.Bytes; + +import static java.lang.Long.numberOfLeadingZeros; +import static java.lang.Long.numberOfTrailingZeros; + +/** + * DirectBitSet with input validations, This class is not thread safe + */ +public class SingleThreadedDirectBitSet implements DirectBitSet { + + public static final long ALL_ONES = ~0L; + + // masks + private Bytes bytes; + private long longLength; + + public SingleThreadedDirectBitSet(Bytes bytes) { + reuse(bytes); + } + + static long singleBit(long bitIndex) { + return 1L << bitIndex; + } + + static long singleBit(int bitIndex) { + return 1L << bitIndex; + } + + static long higherBitsIncludingThis(long bitIndex) { + return ALL_ONES << bitIndex; + } + + // conversions + + static long lowerBitsIncludingThis(long bitIndex) { + return ALL_ONES >>> ~bitIndex; + } + + static long higherBitsExcludingThis(long bitIndex) { + return ~(ALL_ONES >>> ~bitIndex); + } + + static long lowerBitsExcludingThis(long bitIndex) { + return ~(ALL_ONES << bitIndex); + } + + static long longWithThisBit(long bitIndex) { + return bitIndex >> 6; + } + + static long firstByte(long longIndex) { + return longIndex << 3; + } + + static long firstBit(long longIndex) { + return longIndex << 6; + } + + static long lastBit(long longIndex) { + return firstBit(longIndex) + 63; + } + + static void checkNumberOfBits(int numberOfBits) { + if (numberOfBits <= 0 || numberOfBits > 64) + throw new IllegalArgumentException("Illegal number of bits: " + numberOfBits); + } + + // checks + + static boolean checkNotFoundIndex(long fromIndex) { + if (fromIndex < 0) { + if (fromIndex == NOT_FOUND) + return true; + throw new IndexOutOfBoundsException(); + } + return false; + } + + public void reuse(Bytes bytes) { + this.bytes = bytes; + longLength = bytes.capacity() >> 3; + } + + private void checkIndex(long bitIndex, long longIndex) { + if (bitIndex < 0 || longIndex >= longLength) + throw new IndexOutOfBoundsException(); + } + + private void checkFromTo(long fromIndex, long exclusiveToIndex, long toLongIndex) { + if (fromIndex < 0 || fromIndex > exclusiveToIndex || + toLongIndex >= longLength) + throw new IndexOutOfBoundsException(); + } + + private long readLong(long longIndex) { + return bytes.readLong(firstByte(longIndex)); + } + + private void writeLong(long longIndex, long toWrite) { + bytes.writeLong(firstByte(longIndex), toWrite); + } + + @Override + public void reserve() { + bytes.reserve(); + } + + @Override + public boolean release() { + return bytes.release(); + } + + @Override + public int refCount() { + return bytes.refCount(); + } + + @Override + public DirectBitSet flip(long bitIndex) { + long longIndex = longWithThisBit(bitIndex); + checkIndex(bitIndex, longIndex); + long byteIndex = firstByte(longIndex); + long mask = singleBit(bitIndex); + long l = bytes.readLong(byteIndex); + long l2 = l ^ mask; + bytes.writeLong(byteIndex, l2); + return this; + } + + @Override + public DirectBitSet flip(long fromIndex, long exclusiveToIndex) { + long fromLongIndex = longWithThisBit(fromIndex); + long toIndex = exclusiveToIndex - 1; + long toLongIndex = longWithThisBit(toIndex); + checkFromTo(fromIndex, exclusiveToIndex, toLongIndex); + + if (fromLongIndex != toLongIndex) { + long firstFullLongIndex = fromLongIndex; + if ((fromIndex & 63) != 0) { + long fromByteIndex = firstByte(fromLongIndex); + long mask = higherBitsIncludingThis(fromIndex); + long l = bytes.readLong(fromByteIndex); + long l2 = l ^ mask; + bytes.writeLong(fromByteIndex, l2); + firstFullLongIndex++; + } + + if ((exclusiveToIndex & 63) == 0) { + for (long i = firstFullLongIndex; i <= toLongIndex; i++) { + writeLong(i, ~readLong(i)); + } + } else { + for (long i = firstFullLongIndex; i < toLongIndex; i++) { + writeLong(i, ~readLong(i)); + } + + long toByteIndex = firstByte(toLongIndex); + long mask = lowerBitsIncludingThis(toIndex); + long l = bytes.readLong(toByteIndex); + long l2 = l ^ mask; + bytes.writeLong(toByteIndex, l2); + } + } else { + long byteIndex = firstByte(fromLongIndex); + long mask = higherBitsIncludingThis(fromIndex) & lowerBitsIncludingThis(toIndex); + long l = bytes.readLong(byteIndex); + long l2 = l ^ mask; + bytes.writeLong(byteIndex, l2); + } + return this; + } + + @Override + public DirectBitSet set(long bitIndex) { + long longIndex = longWithThisBit(bitIndex); + checkIndex(bitIndex, longIndex); + long byteIndex = firstByte(longIndex); + long mask = singleBit(bitIndex); + long l = bytes.readLong(byteIndex); + if ((l & mask) != 0) return this; + long l2 = l | mask; + bytes.writeLong(byteIndex, l2); + return this; + } + + @Override + public boolean setIfClear(long bitIndex) { + long longIndex = longWithThisBit(bitIndex); + checkIndex(bitIndex, longIndex); + long byteIndex = firstByte(longIndex); + long mask = singleBit(bitIndex); + long l = bytes.readLong(byteIndex); + long l2 = l | mask; + if (l == l2) + return false; + bytes.writeLong(byteIndex, l2); + return true; + } + + @Override + public DirectBitSet set(long bitIndex, boolean value) { + return value ? set(bitIndex) : clear(bitIndex); + } + + @Override + public DirectBitSet set(long fromIndex, long exclusiveToIndex) { + long fromLongIndex = longWithThisBit(fromIndex); + long toIndex = exclusiveToIndex - 1; + long toLongIndex = longWithThisBit(toIndex); + checkFromTo(fromIndex, exclusiveToIndex, toLongIndex); + + if (fromLongIndex != toLongIndex) { + long firstFullLongIndex = fromLongIndex; + if ((fromIndex & 63) != 0) { + long fromByteIndex = firstByte(fromLongIndex); + long mask = higherBitsIncludingThis(fromIndex); + long l = bytes.readLong(fromByteIndex); + long l2 = l | mask; + bytes.writeLong(fromByteIndex, l2); + firstFullLongIndex++; + } + + if ((exclusiveToIndex & 63) == 0) { + for (long i = firstFullLongIndex; i <= toLongIndex; i++) { + writeLong(i, ALL_ONES); + } + } else { + for (long i = firstFullLongIndex; i < toLongIndex; i++) { + writeLong(i, ALL_ONES); + } + + long toByteIndex = firstByte(toLongIndex); + long mask = lowerBitsIncludingThis(toIndex); + long l = bytes.readLong(toByteIndex); + long l2 = l | mask; + bytes.writeLong(toByteIndex, l2); + } + } else { + long byteIndex = firstByte(fromLongIndex); + long mask = higherBitsIncludingThis(fromIndex) & lowerBitsIncludingThis(toIndex); + long l = bytes.readLong(byteIndex); + long l2 = l | mask; + bytes.writeLong(byteIndex, l2); + } + return this; + } + + @Override + public DirectBitSet setAll() { + for (long i = 0; i < longLength; i++) { + writeLong(i, ALL_ONES); + } + return this; + } + + @Override + public DirectBitSet set(long fromIndex, long toIndex, boolean value) { + return value ? set(fromIndex, toIndex) : clear(fromIndex, toIndex); + } + + @Override + public DirectBitSet clear(long bitIndex) { + long longIndex = longWithThisBit(bitIndex); + checkIndex(bitIndex, longIndex); + long byteIndex = firstByte(longIndex); + long mask = singleBit(bitIndex); + long l = bytes.readLong(byteIndex); + if ((l & mask) == 0) return this; + long l2 = l & ~mask; + bytes.writeLong(byteIndex, l2); + return this; + } + + @Override + public boolean clearIfSet(long bitIndex) { + long longIndex = longWithThisBit(bitIndex); + checkIndex(bitIndex, longIndex); + long byteIndex = firstByte(longIndex); + long mask = singleBit(bitIndex); + long l = bytes.readLong(byteIndex); + if ((l & mask) == 0) return false; + long l2 = l & ~mask; + bytes.writeLong(byteIndex, l2); + return true; + } + + @Override + public DirectBitSet clear(long fromIndex, long exclusiveToIndex) { + long fromLongIndex = longWithThisBit(fromIndex); + long toIndex = exclusiveToIndex - 1; + long toLongIndex = longWithThisBit(toIndex); + checkFromTo(fromIndex, exclusiveToIndex, toLongIndex); + + if (fromLongIndex != toLongIndex) { + long firstFullLongIndex = fromLongIndex; + if ((fromIndex & 63) != 0) { + long fromByteIndex = firstByte(fromLongIndex); + long mask = higherBitsIncludingThis(fromIndex); + long l = bytes.readLong(fromByteIndex); + long l2 = l & ~mask; + bytes.writeLong(fromByteIndex, l2); + firstFullLongIndex++; + } + + if ((exclusiveToIndex & 63) == 0) { + for (long i = firstFullLongIndex; i <= toLongIndex; i++) { + writeLong(i, 0L); + } + } else { + for (long i = firstFullLongIndex; i < toLongIndex; i++) { + writeLong(i, 0L); + } + + long toByteIndex = firstByte(toLongIndex); + long mask = lowerBitsIncludingThis(toIndex); + long l = bytes.readLong(toByteIndex); + long l2 = l & ~mask; + bytes.writeLong(toByteIndex, l2); + } + } else { + long byteIndex = firstByte(fromLongIndex); + long mask = higherBitsIncludingThis(fromIndex) & lowerBitsIncludingThis(toIndex); + long l = bytes.readLong(byteIndex); + long l2 = l & ~mask; + bytes.writeLong(byteIndex, l2); + } + return this; + } + + /** + * Checks if each bit from the specified {@code fromIndex} (inclusive) to the specified {@code + * exclusiveToIndex} is set to {@code true}. + * + * @param fromIndex index of the first bit to check + * @param exclusiveToIndex index after the last bit to check + * @return {@code true} if all bits in the specified range are set to {@code true}, {@code + * false} otherwise + * @throws IndexOutOfBoundsException if {@code fromIndex} is negative, or {@code fromIndex} is + * larger than {@code toIndex}, or {@code exclusiveToIndex} is + * larger or equal to {@code size()} + */ + public boolean allSet(long fromIndex, long exclusiveToIndex) { + long fromLongIndex = longWithThisBit(fromIndex); + long toIndex = exclusiveToIndex - 1; + long toLongIndex = longWithThisBit(toIndex); + checkFromTo(fromIndex, exclusiveToIndex, toLongIndex); + + if (fromLongIndex != toLongIndex) { + long firstFullLongIndex = fromLongIndex; + if ((fromIndex & 63) != 0) { + long mask = higherBitsIncludingThis(fromIndex); + if ((~(readLong(fromLongIndex)) & mask) != 0L) + return false; + firstFullLongIndex++; + } + + if ((exclusiveToIndex & 63) == 0) { + for (long i = firstFullLongIndex; i <= toLongIndex; i++) { + if (~readLong(i) != 0L) + return false; + } + return true; + + } else { + for (long i = firstFullLongIndex; i < toLongIndex; i++) { + if (~readLong(i) != 0L) + return false; + } + + long mask = lowerBitsIncludingThis(toIndex); + return ((~readLong(toLongIndex)) & mask) == 0L; + } + } else { + long mask = higherBitsIncludingThis(fromIndex) & lowerBitsIncludingThis(toIndex); + return ((~readLong(fromLongIndex)) & mask) == 0L; + } + } + + /** + * Checks if each bit from the specified {@code fromIndex} (inclusive) to the specified {@code + * exclusiveToIndex} is set to {@code false}. + * + * @param fromIndex index of the first bit to check + * @param exclusiveToIndex index after the last bit to check + * @return {@code true} if all bits in the specified range are set to {@code false}, {@code + * false} otherwise + * @throws IndexOutOfBoundsException if {@code fromIndex} is negative, or {@code fromIndex} is + * larger than {@code toIndex}, or {@code exclusiveToIndex} is + * larger or equal to {@code size()} + */ + public boolean allClear(long fromIndex, long exclusiveToIndex) { + long fromLongIndex = longWithThisBit(fromIndex); + long toIndex = exclusiveToIndex - 1; + long toLongIndex = longWithThisBit(toIndex); + checkFromTo(fromIndex, exclusiveToIndex, toLongIndex); + + if (fromLongIndex != toLongIndex) { + long firstFullLongIndex = fromLongIndex; + if ((fromIndex & 63) != 0) { + long mask = higherBitsIncludingThis(fromIndex); + if ((readLong(fromLongIndex) & mask) != 0L) + return false; + firstFullLongIndex++; + } + + if ((exclusiveToIndex & 63) == 0) { + for (long i = firstFullLongIndex; i <= toLongIndex; i++) { + if (readLong(i) != 0L) + return false; + } + return true; + + } else { + for (long i = firstFullLongIndex; i < toLongIndex; i++) { + if (readLong(i) != 0L) + return false; + } + + long mask = lowerBitsIncludingThis(toIndex); + return (readLong(toLongIndex) & mask) == 0L; + } + } else { + long mask = higherBitsIncludingThis(fromIndex) & lowerBitsIncludingThis(toIndex); + return (readLong(fromLongIndex) & mask) == 0L; + } + } + + @Override + public DirectBitSet clear() { + bytes.zeroOut(); + return this; + } + + @Override + public boolean get(long bitIndex) { + long longIndex = longWithThisBit(bitIndex); + checkIndex(bitIndex, longIndex); + long l = readLong(longIndex); + return (l & (singleBit(bitIndex))) != 0; + } + + @Override + public boolean isSet(long bitIndex) { + return get(bitIndex); + } + + @Override + public boolean isClear(long bitIndex) { + return !get(bitIndex); + } + + @Override + public long getLong(long longIndex) { + checkIndex(longIndex, longIndex); + return readLong(longIndex); + } + + @Override + public long nextSetBit(long fromIndex) { + if (fromIndex < 0) + throw new IndexOutOfBoundsException(); + long fromLongIndex = longWithThisBit(fromIndex); + if (fromLongIndex >= longLength) + return NOT_FOUND; + long l = readLong(fromLongIndex) >>> fromIndex; + if (l != 0) { + return fromIndex + numberOfTrailingZeros(l); + } + for (long i = fromLongIndex + 1; i < longLength; i++) { + l = readLong(i); + if (l != 0) + return firstBit(i) + numberOfTrailingZeros(l); + } + return NOT_FOUND; + } + + @Override + public Bits setBits() { + return new SetBits(); + } + + @Override + public long clearNextSetBit(long fromIndex) { + if (fromIndex < 0) + throw new IndexOutOfBoundsException(); + long fromLongIndex = longWithThisBit(fromIndex); + if (fromLongIndex >= longLength) + return NOT_FOUND; + long fromByteIndex = firstByte(fromLongIndex); + long w = bytes.readLong(fromByteIndex); + long l = w >>> fromIndex; + if (l != 0) { + long indexOfSetBit = fromIndex + numberOfTrailingZeros(l); + long mask = singleBit(indexOfSetBit); + bytes.writeLong(fromByteIndex, w ^ mask); + return indexOfSetBit; + } + for (long i = fromLongIndex + 1; i < longLength; i++) { + long byteIndex = firstByte(i); + l = bytes.readLong(byteIndex); + if (l != 0) { + long indexOfSetBit = firstBit(i) + numberOfTrailingZeros(l); + long mask = singleBit(indexOfSetBit); + bytes.writeLong(byteIndex, l ^ mask); + return indexOfSetBit; + } + } + return NOT_FOUND; + } + + @Override + public long nextSetLong(long fromLongIndex) { + if (fromLongIndex < 0) + throw new IndexOutOfBoundsException(); + if (fromLongIndex >= longLength) + return NOT_FOUND; + if (readLong(fromLongIndex) != 0) + return fromLongIndex; + for (long i = fromLongIndex + 1; i < longLength; i++) { + if (readLong(i) != 0) + return i; + } + return NOT_FOUND; + } + + @Override + public long nextClearBit(long fromIndex) { + if (fromIndex < 0) + throw new IndexOutOfBoundsException(); + long fromLongIndex = longWithThisBit(fromIndex); + if (fromLongIndex >= longLength) + return NOT_FOUND; + long l = (~readLong(fromLongIndex)) >>> fromIndex; + if (l != 0) { + return fromIndex + numberOfTrailingZeros(l); + } + for (long i = fromLongIndex + 1; i < longLength; i++) { + l = ~readLong(i); + if (l != 0) + return firstBit(i) + numberOfTrailingZeros(l); + } + return NOT_FOUND; + } + + @Override + public long setNextClearBit(long fromIndex) { + if (fromIndex < 0) + throw new IndexOutOfBoundsException(); + long fromLongIndex = longWithThisBit(fromIndex); + if (fromLongIndex >= longLength) + return NOT_FOUND; + long fromByteIndex = firstByte(fromLongIndex); + long w = bytes.readLong(fromByteIndex); + long l = (~w) >>> fromIndex; + if (l != 0) { + long indexOfClearBit = fromIndex + numberOfTrailingZeros(l); + long mask = singleBit(indexOfClearBit); + bytes.writeLong(fromByteIndex, w ^ mask); + return indexOfClearBit; + } + for (long i = fromLongIndex + 1; i < longLength; i++) { + long byteIndex = firstByte(i); + w = bytes.readLong(byteIndex); + l = ~w; + if (l != 0) { + long indexOfClearBit = firstBit(i) + numberOfTrailingZeros(l); + long mask = singleBit(indexOfClearBit); + bytes.writeLong(byteIndex, w ^ mask); + return indexOfClearBit; + } + } + return NOT_FOUND; + } + + @Override + public long nextClearLong(long fromLongIndex) { + if (fromLongIndex < 0) + throw new IndexOutOfBoundsException(); + if (fromLongIndex >= longLength) + return NOT_FOUND; + if (readLong(fromLongIndex) != ALL_ONES) + return fromLongIndex; + for (long i = fromLongIndex + 1; i < longLength; i++) { + if (readLong(i) != ALL_ONES) + return i; + } + return NOT_FOUND; + } + + @Override + public long previousSetBit(long fromIndex) { + if (checkNotFoundIndex(fromIndex)) + return NOT_FOUND; + long fromLongIndex = longWithThisBit(fromIndex); + if (fromLongIndex >= longLength) { + // the same policy for this "index out of bounds" situation + // as in j.u.BitSet + fromLongIndex = longLength - 1; + fromIndex = size() - 1; + } + // << ~fromIndex === << (63 - (fromIndex & 63)) + long l = readLong(fromLongIndex) << ~fromIndex; + if (l != 0) + return fromIndex - numberOfLeadingZeros(l); + for (long i = fromLongIndex - 1; i >= 0; i--) { + l = readLong(i); + if (l != 0) + return lastBit(i) - numberOfLeadingZeros(l); + } + return NOT_FOUND; + } + + private long previousSetBit(long fromIndex, long inclusiveToIndex) { + long fromLongIndex = longWithThisBit(fromIndex); + long toLongIndex = longWithThisBit(inclusiveToIndex); + checkFromTo(inclusiveToIndex, fromIndex + 1, toLongIndex); + if (fromLongIndex >= longLength) { + // the same policy for this "index out of bounds" situation + // as in j.u.BitSet + fromLongIndex = longLength - 1; + fromIndex = size() - 1; + } + if (fromLongIndex != toLongIndex) { + // << ~fromIndex === << (63 - (fromIndex & 63)) + long l = readLong(fromLongIndex) << ~fromIndex; + if (l != 0) + return fromIndex - numberOfLeadingZeros(l); + for (long i = fromLongIndex - 1; i > toLongIndex; i--) { + l = readLong(i); + if (l != 0) + return lastBit(i) - numberOfLeadingZeros(l); + } + fromIndex = lastBit(toLongIndex); + } + long w = readLong(toLongIndex); + long mask = higherBitsIncludingThis(inclusiveToIndex) & lowerBitsIncludingThis(fromIndex); + long l = w & mask; + if (l != 0) { + return lastBit(toLongIndex) - numberOfLeadingZeros(l); + } + return NOT_FOUND; + } + + @Override + public long clearPreviousSetBit(long fromIndex) { + if (checkNotFoundIndex(fromIndex)) + return NOT_FOUND; + long fromLongIndex = longWithThisBit(fromIndex); + if (fromLongIndex >= longLength) { + fromLongIndex = longLength - 1; + fromIndex = size() - 1; + } + long fromByteIndex = firstByte(fromLongIndex); + long w = bytes.readLong(fromByteIndex); + long l = w << ~fromIndex; + if (l != 0) { + long indexOfSetBit = fromIndex - numberOfLeadingZeros(l); + long mask = singleBit(indexOfSetBit); + bytes.writeLong(fromByteIndex, w ^ mask); + return indexOfSetBit; + } + for (long i = fromLongIndex - 1; i >= 0; i--) { + long byteIndex = firstByte(i); + l = bytes.readLong(byteIndex); + if (l != 0) { + long indexOfSetBit = lastBit(i) - numberOfLeadingZeros(l); + long mask = singleBit(indexOfSetBit); + bytes.writeLong(byteIndex, l ^ mask); + return indexOfSetBit; + } + } + return NOT_FOUND; + } + + @Override + public long previousSetLong(long fromLongIndex) { + if (checkNotFoundIndex(fromLongIndex)) + return NOT_FOUND; + if (fromLongIndex >= longLength) + fromLongIndex = longLength - 1; + if (readLong(fromLongIndex) != 0) + return fromLongIndex; + for (long i = fromLongIndex - 1; i >= 0; i--) { + if (readLong(i) != 0) + return i; + } + return NOT_FOUND; + } + + @Override + public long previousClearBit(long fromIndex) { + if (checkNotFoundIndex(fromIndex)) + return NOT_FOUND; + long fromLongIndex = longWithThisBit(fromIndex); + if (fromLongIndex >= longLength) { + fromLongIndex = longLength - 1; + fromIndex = size() - 1; + } + long l = (~readLong(fromLongIndex)) << ~fromIndex; + if (l != 0) + return fromIndex - numberOfLeadingZeros(l); + for (long i = fromLongIndex - 1; i >= 0; i--) { + l = ~readLong(i); + if (l != 0) + return lastBit(i) - numberOfLeadingZeros(l); + } + return NOT_FOUND; + } + + @Override + public long setPreviousClearBit(long fromIndex) { + if (checkNotFoundIndex(fromIndex)) + return NOT_FOUND; + long fromLongIndex = longWithThisBit(fromIndex); + if (fromLongIndex >= longLength) { + fromLongIndex = longLength - 1; + fromIndex = size() - 1; + } + long fromByteIndex = firstByte(fromLongIndex); + long w = bytes.readLong(fromByteIndex); + long l = (~w) << ~fromIndex; + if (l != 0) { + long indexOfClearBit = fromIndex - numberOfLeadingZeros(l); + long mask = singleBit(indexOfClearBit); + bytes.writeLong(fromByteIndex, w ^ mask); + return indexOfClearBit; + } + for (long i = fromLongIndex - 1; i >= 0; i--) { + long byteIndex = firstByte(i); + w = bytes.readLong(byteIndex); + l = ~w; + if (l != 0) { + long indexOfClearBit = lastBit(i) - numberOfLeadingZeros(l); + long mask = singleBit(indexOfClearBit); + bytes.writeLong(byteIndex, w ^ mask); + return indexOfClearBit; + } + } + return NOT_FOUND; + } + + @Override + public long previousClearLong(long fromLongIndex) { + if (checkNotFoundIndex(fromLongIndex)) + return NOT_FOUND; + if (fromLongIndex >= longLength) + fromLongIndex = longLength - 1; + if (readLong(fromLongIndex) != ALL_ONES) + return fromLongIndex; + for (long i = fromLongIndex - 1; i >= 0; i--) { + if (readLong(i) != ALL_ONES) + return i; + } + return NOT_FOUND; + } + + @Override + public long size() { + return longLength << 6; + } + + @Override + public long cardinality() { + long count = 0; + for (long i = 0; i < longLength; i++) { + count += Long.bitCount(readLong(i)); + } + return count; + } + + @Override + public DirectBitSet and(long longIndex, long value) { + long l = readLong(longIndex); + long l2 = l & value; + writeLong(longIndex, l2); + return this; + } + + @Override + public DirectBitSet or(long longIndex, long value) { + long l = readLong(longIndex); + long l2 = l | value; + writeLong(longIndex, l2); + return this; + } + + @Override + public DirectBitSet xor(long longIndex, long value) { + long l = readLong(longIndex); + long l2 = l ^ value; + writeLong(longIndex, l2); + return this; + } + + @Override + public DirectBitSet andNot(long longIndex, long value) { + long l = readLong(longIndex); + long l2 = l & ~value; + writeLong(longIndex, l2); + return this; + } + + /** + * @throws IllegalArgumentException if {@code numberOfBits} is negative + */ + @Override + public long setNextNContinuousClearBits(long fromIndex, int numberOfBits) { + if (numberOfBits > 64) + return setNextManyContinuousClearBits(fromIndex, numberOfBits); + checkNumberOfBits(numberOfBits); + if (numberOfBits == 1) + return setNextClearBit(fromIndex); + if (fromIndex < 0) + throw new IndexOutOfBoundsException(); + + long nTrailingOnes = ALL_ONES >>> (64 - numberOfBits); + + long bitIndex = fromIndex; + long longIndex2 = longWithThisBit(bitIndex); + if (longIndex2 >= longLength) + return NOT_FOUND; + int bitsFromFirstWord = 64 - (((int) bitIndex) & 63); + long byteIndex2 = firstByte(longIndex2); + long w1, w2 = bytes.readLong(byteIndex2); + longLoop: + while (true) { + w1 = w2; + byteIndex2 += 8; + if (++longIndex2 < longLength) { + w2 = bytes.readLong(byteIndex2); + + } else if (longIndex2 == longLength) { + w2 = ALL_ONES; + + } else { + return NOT_FOUND; + } + long l; + // (1) + if (bitsFromFirstWord != 64) { + l = (w1 >>> bitIndex) | (w2 << bitsFromFirstWord); + + } else { + // special case, because if bitsFromFirstWord is 64 + // w2 shift is overflowed + l = w1; + } + // (2) + if ((l & 1) != 0) { + long x = ~l; + if (x != 0) { + int trailingOnes = numberOfTrailingZeros(x); + bitIndex += trailingOnes; + // (3) + if ((bitsFromFirstWord -= trailingOnes) <= 0) { + bitsFromFirstWord += 64; + continue; // long loop + } + l = (w1 >>> bitIndex) | (w2 << bitsFromFirstWord); + + } else { + // all bits are ones, skip a whole word, + // bitsFromFirstWord not changed + bitIndex += 64; + continue; // long loop + } + } + while (true) { + if ((l & nTrailingOnes) == 0) { + long mask1 = nTrailingOnes << bitIndex; + bytes.writeLong(byteIndex2 - 8, w1 ^ mask1); + int bitsFromSecondWordToSwitch = + numberOfBits - bitsFromFirstWord; + if (bitsFromSecondWordToSwitch > 0) { + long mask2 = (singleBit(bitsFromSecondWordToSwitch)) - 1; + bytes.writeLong(byteIndex2, w2 ^ mask2); + } + return bitIndex; + } + // n > trailing zeros > 0 + // > 0 ensured by block (2) + int trailingZeros = numberOfTrailingZeros(l); + bitIndex += trailingZeros; + // (4) + if ((bitsFromFirstWord -= trailingZeros) <= 0) { + bitsFromFirstWord += 64; + continue longLoop; + } + // (5) + // subtractions (3) and (4) together ensure that + // bitsFromFirstWord != 64, => no need in condition like (1) + l = (w1 >>> bitIndex) | (w2 << bitsFromFirstWord); + + long x = ~l; + if (x != 0) { + int trailingOnes = numberOfTrailingZeros(x); + bitIndex += trailingOnes; + if ((bitsFromFirstWord -= trailingOnes) <= 0) { + bitsFromFirstWord += 64; + continue longLoop; + } + // same as (5) + l = (w1 >>> bitIndex) | (w2 << bitsFromFirstWord); + + } else { + // all bits are ones, skip a whole word, + // bitsFromFirstWord not changed + bitIndex += 64; + continue longLoop; + } + } + } + } + + private long setNextManyContinuousClearBits(long fromIndex, int numberOfBits) { + long size = size(); + long testFromIndex = fromIndex; + while (true) { + long limit = fromIndex + numberOfBits; + if (limit > size) + return NOT_FOUND; + long needToBeZerosUntil = limit - 1; + long lastSetBit = previousSetBit(needToBeZerosUntil, testFromIndex); + if (lastSetBit == NOT_FOUND) { + set(fromIndex, limit); + return fromIndex; + } + fromIndex = lastSetBit + 1; + testFromIndex = limit; + } + } + + /** + * @throws IllegalArgumentException if {@code numberOfBits} is out of range {@code 0 < + * numberOfBits && numberOfBits <= 64} + */ + @Override + public long clearNextNContinuousSetBits(long fromIndex, int numberOfBits) { + checkNumberOfBits(numberOfBits); + if (numberOfBits == 1) + return clearNextSetBit(fromIndex); + if (fromIndex < 0) + throw new IndexOutOfBoundsException(); + + long nTrailingOnes = ALL_ONES >>> (64 - numberOfBits); + + long bitIndex = fromIndex; + long longIndex2 = longWithThisBit(bitIndex); + if (longIndex2 >= longLength) + return NOT_FOUND; + int bitsFromFirstWord = 64 - (((int) bitIndex) & 63); + long byteIndex2 = firstByte(longIndex2); + long w1, w2 = bytes.readLong(byteIndex2); + longLoop: + while (true) { + w1 = w2; + byteIndex2 += 8; + if (++longIndex2 < longLength) { + w2 = bytes.readLong(byteIndex2); + + } else if (longIndex2 == longLength) { + w2 = 0L; + + } else { + return NOT_FOUND; + } + long l; + // (1) + if (bitsFromFirstWord != 64) { + l = (w1 >>> bitIndex) | (w2 << bitsFromFirstWord); + + } else { + // special case, because if bitsFromFirstWord is 64 + // w2 shift is overflowed + l = w1; + } + // (2) + if ((l & 1) == 0) { + if (l != 0) { + int trailingZeros = numberOfTrailingZeros(l); + bitIndex += trailingZeros; + // (3) + if ((bitsFromFirstWord -= trailingZeros) <= 0) { + bitsFromFirstWord += 64; + continue; // long loop + } + l = (w1 >>> bitIndex) | (w2 << bitsFromFirstWord); + + } else { + // all bits are zeros, skip a whole word, + // bitsFromFirstWord not changed + bitIndex += 64; + continue; // long loop + } + } + while (true) { + if (((~l) & nTrailingOnes) == 0) { + long mask1 = nTrailingOnes << bitIndex; + bytes.writeLong(byteIndex2 - 8, w1 ^ mask1); + int bitsFromSecondWordToSwitch = + numberOfBits - bitsFromFirstWord; + if (bitsFromSecondWordToSwitch > 0) { + long mask2 = (singleBit(bitsFromSecondWordToSwitch)) - 1; + bytes.writeLong(byteIndex2, w2 ^ mask2); + } + return bitIndex; + } + // n > trailing ones > 0 + // > 0 ensured by block (2) + int trailingOnes = numberOfTrailingZeros(~l); + bitIndex += trailingOnes; + // (4) + if ((bitsFromFirstWord -= trailingOnes) <= 0) { + bitsFromFirstWord += 64; + continue longLoop; + } + // (5) + // subtractions (3) and (4) together ensure that + // bitsFromFirstWord != 64, => no need in condition like (1) + l = (w1 >>> bitIndex) | (w2 << bitsFromFirstWord); + + if (l != 0) { + int trailingZeros = numberOfTrailingZeros(l); + bitIndex += trailingZeros; + if ((bitsFromFirstWord -= trailingZeros) <= 0) { + bitsFromFirstWord += 64; + continue longLoop; + } + // same as (5) + l = (w1 >>> bitIndex) | (w2 << bitsFromFirstWord); + + } else { + // all bits are zeros, skip a whole word, + // bitsFromFirstWord not changed + bitIndex += 64; + continue longLoop; + } + } + } + } + + /** + * @throws IllegalArgumentException if {@code numberOfBits} is out of range {@code 0 < + * numberOfBits && numberOfBits <= 64} + */ + @Override + public long setPreviousNContinuousClearBits( + long fromIndex, int numberOfBits) { + checkNumberOfBits(numberOfBits); + if (numberOfBits == 1) + return setPreviousClearBit(fromIndex); + if (checkNotFoundIndex(fromIndex)) + return NOT_FOUND; + + int n64Complement = 64 - numberOfBits; + long nLeadingOnes = higherBitsIncludingThis(n64Complement); + + long higherBitBound = fromIndex + 1; + long lowLongIndex = longWithThisBit(fromIndex); + if (lowLongIndex >= longLength) { + lowLongIndex = longLength - 1; + higherBitBound = longLength << 6; + } + int bitsFromLowWord = (64 - (((int) higherBitBound) & 63)) & 63; + long lowByteIndex = firstByte(lowLongIndex); + // low word, high word + long hw, lw = bytes.readLong(lowByteIndex); + longLoop: + while (true) { + hw = lw; + lowByteIndex -= 8; + if (--lowLongIndex >= 0) { + lw = bytes.readLong(lowByteIndex); + + } else if (lowLongIndex == -1) { + lw = ALL_ONES; + + } else { + return NOT_FOUND; + } + long l; + if (bitsFromLowWord != 0) { // (1) + l = (lw >>> higherBitBound) | (hw << bitsFromLowWord); + + } else { + // all bits from high word, special case needed because + // higherBitBound is multiple of 64 and lw not shifted away + l = hw; + } + // (2) + if (l < 0) { // condition means the highest bit is one + long x = ~l; + if (x != 0) { + int leadingOnes = numberOfLeadingZeros(x); + higherBitBound -= leadingOnes; + bitsFromLowWord += leadingOnes; // (3) + int flw; + if ((flw = bitsFromLowWord - 64) >= 0) { + bitsFromLowWord = flw; + continue; // long loop + } + l = (lw >>> higherBitBound) | (hw << bitsFromLowWord); + + } else { + // all bits are ones, skip a whole word, + // bitsFromLowWord not changed + higherBitBound -= 64; + continue; // long loop + } + } + while (true) { + if ((l & nLeadingOnes) == 0) { + long hMask = nLeadingOnes >>> bitsFromLowWord; + bytes.writeLong(lowByteIndex + 8, hw ^ hMask); + // bitsFromLow - (64 - n) = n - (64 - bitsFromLow) = + // = n - bitsFromHigh + int bitsFromLowWordToSwitch = + bitsFromLowWord - n64Complement; + if (bitsFromLowWordToSwitch > 0) { + long lMask = ~(ALL_ONES >>> bitsFromLowWordToSwitch); + bytes.writeLong(lowByteIndex, lw ^ lMask); + } + return higherBitBound - numberOfBits; + } + // n > leading zeros > 0 + // > 0 ensured by block (2) + int leadingZeros = numberOfLeadingZeros(l); + higherBitBound -= leadingZeros; + bitsFromLowWord += leadingZeros; // (4) + int flw; + if ((flw = bitsFromLowWord - 64) >= 0) { + bitsFromLowWord = flw; + continue longLoop; + } + // (5) + // additions (3) and (4) together ensure that + // bitsFromFirstWord > 0, => no need in condition like (1) + l = (lw >>> higherBitBound) | (hw << bitsFromLowWord); + + long x = ~l; + if (x != 0) { + int leadingOnes = numberOfLeadingZeros(x); + higherBitBound -= leadingOnes; + bitsFromLowWord += leadingOnes; + if ((flw = bitsFromLowWord - 64) >= 0) { + bitsFromLowWord = flw; + continue longLoop; + } + // same as (5) + l = (lw >>> higherBitBound) | (hw << bitsFromLowWord); + + } else { + // all bits are ones, skip a whole word, + // bitsFromLowWord not changed + higherBitBound -= 64; + continue longLoop; + } + } + } + } + + /** + * @throws IllegalArgumentException if {@code numberOfBits} is out of range {@code 0 < + * numberOfBits && numberOfBits <= 64} + */ + @Override + public long clearPreviousNContinuousSetBits( + long fromIndex, int numberOfBits) { + checkNumberOfBits(numberOfBits); + if (numberOfBits == 1) + return clearPreviousSetBit(fromIndex); + if (checkNotFoundIndex(fromIndex)) + return NOT_FOUND; + + int n64Complement = 64 - numberOfBits; + long nLeadingOnes = higherBitsIncludingThis(n64Complement); + + long higherBitBound = fromIndex + 1; + long lowLongIndex = longWithThisBit(fromIndex); + if (lowLongIndex >= longLength) { + lowLongIndex = longLength - 1; + higherBitBound = longLength << 6; + } + int bitsFromLowWord = (64 - (((int) higherBitBound) & 63)) & 63; + long lowByteIndex = firstByte(lowLongIndex); + // low word, high word + long hw, lw = bytes.readLong(lowByteIndex); + longLoop: + while (true) { + hw = lw; + lowByteIndex -= 8; + if (--lowLongIndex >= 0) { + lw = bytes.readLong(lowByteIndex); + + } else if (lowLongIndex == -1) { + lw = 0L; + + } else { + return NOT_FOUND; + } + long l; + if (bitsFromLowWord != 0) { // (1) + l = (lw >>> higherBitBound) | (hw << bitsFromLowWord); + + } else { + // all bits from high word, special case needed because + // higherBitBound is multiple of 64 and lw not shifted away + l = hw; + } + // (2) + if (l > 0) { // condition means the highest bit is zero, but not all + int leadingZeros = numberOfLeadingZeros(l); + higherBitBound -= leadingZeros; + bitsFromLowWord += leadingZeros; // (3) + int flw; + if ((flw = bitsFromLowWord - 64) >= 0) { + bitsFromLowWord = flw; + continue; // long loop + } + l = (lw >>> higherBitBound) | (hw << bitsFromLowWord); + + } else if (l == 0) { + // all bits are zeros, skip a whole word, + // bitsFromLowWord not changed + higherBitBound -= 64; + continue; // long loop + } + while (true) { + if (((~l) & nLeadingOnes) == 0) { + long hMask = nLeadingOnes >>> bitsFromLowWord; + bytes.writeLong(lowByteIndex + 8, hw ^ hMask); + // bitsFromLow - (64 - n) = n - (64 - bitsFromLow) = + // = n - bitsFromHigh + int bitsFromLowWordToSwitch = + bitsFromLowWord - n64Complement; + if (bitsFromLowWordToSwitch > 0) { + long lMask = ~(ALL_ONES >>> bitsFromLowWordToSwitch); + bytes.writeLong(lowByteIndex, lw ^ lMask); + } + return higherBitBound - numberOfBits; + } + // n > leading ones > 0 + // > 0 ensured by block (2) + int leadingOnes = numberOfLeadingZeros(~l); + higherBitBound -= leadingOnes; + bitsFromLowWord += leadingOnes; // (4) + int flw; + if ((flw = bitsFromLowWord - 64) >= 0) { + bitsFromLowWord = flw; + continue longLoop; + } + // (5) + // additions (3) and (4) together ensure that + // bitsFromFirstWord > 0, => no need in condition like (1) + l = (lw >>> higherBitBound) | (hw << bitsFromLowWord); + + if (l != 0) { + int leadingZeros = numberOfLeadingZeros(l); + higherBitBound -= leadingZeros; + bitsFromLowWord += leadingZeros; + if ((flw = bitsFromLowWord - 64) >= 0) { + bitsFromLowWord = flw; + continue longLoop; + } + // same as (5) + l = (lw >>> higherBitBound) | (hw << bitsFromLowWord); + + } else { + // all bits are zeros, skip a whole word, + // bitsFromLowWord not changed + higherBitBound -= 64; + continue longLoop; + } + } + } + } + + @Override + public String toString() { + StringBuilder sb = new StringBuilder(); + sb.append("SingleThreadedBitSet{"); + sb.append("size=" + size()); + sb.append(", cardinality=" + cardinality()); + sb.append(", bits=["); + for (long i = 0L; i < longLength; i++) { + sb.append(Long.toBinaryString(readLong(i))); + } + sb.append("]}"); + return sb.toString(); + } + + private class SetBits implements Bits { + private final long byteLength = longLength << 3; + private long byteIndex = 0; + private long bitIndex = -1; + private long currentWord = bytes.readLong(0); + + @Override + public long next() { + long l; + if ((l = currentWord) != 0) { + int trailingZeros = numberOfTrailingZeros(l); + currentWord = (l >>> trailingZeros) >>> 1; + return bitIndex += trailingZeros + 1; + } + for (long i = byteIndex, lim = byteLength; (i += 8) < lim; ) { + if ((l = bytes.readLong(i)) != 0) { + byteIndex = i; + int trailingZeros = numberOfTrailingZeros(l); + currentWord = (l >>> trailingZeros) >>> 1; + return bitIndex = (i << 3) + trailingZeros; + } + } + currentWord = 0; + byteIndex = byteLength; + return -1; + } + } +} diff --git a/lang/src/main/java/net/openhft/lang/collection/impl/HugeArrayImpl.java b/lang/src/main/java/net/openhft/lang/collection/impl/HugeArrayImpl.java index 97cf6dc..e3ac007 100644 --- a/lang/src/main/java/net/openhft/lang/collection/impl/HugeArrayImpl.java +++ b/lang/src/main/java/net/openhft/lang/collection/impl/HugeArrayImpl.java @@ -1,17 +1,17 @@ /* - * Copyright 2013 Peter Lawrey + * Copyright (C) 2015 higherfrequencytrading.com * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as published by + * the Free Software Foundation, either version 3 of the License. * - * http://www.apache.org/licenses/LICENSE-2.0 + * 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 Lesser General Public License for more details. * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. + * You should have received a copy of the GNU Lesser General Public License + * along with this program. If not, see <http://www.gnu.org/licenses/>. */ package net.openhft.lang.collection.impl; @@ -21,7 +21,7 @@ import net.openhft.lang.io.DirectBytes; import net.openhft.lang.io.DirectStore; import net.openhft.lang.model.Byteable; import net.openhft.lang.model.Copyable; -import net.openhft.lang.model.DataValueGenerator; +import net.openhft.lang.model.DataValueClasses; import java.util.ArrayList; import java.util.List; @@ -31,29 +31,26 @@ import java.util.List; */ public class HugeArrayImpl<T> implements HugeArray<T> { private static final int MAX_SIZE = 10; - - private final DataValueGenerator valueGenerator; private final Class<T> tClass; private final long length; private final int size; private final DirectStore store; private final List<T> freeList = new ArrayList<T>(MAX_SIZE); - public HugeArrayImpl(DataValueGenerator valueGenerator, Class<T> tClass, long length) { - this.valueGenerator = valueGenerator; + public HugeArrayImpl(Class<T> tClass, long length) { this.tClass = tClass; this.length = length; - T ref = valueGenerator.nativeInstance(tClass); + T ref = DataValueClasses.newDirectReference(tClass); size = ((Byteable) ref).maxSize(); - store = new DirectStore(null, length * size); - ((Byteable) ref).bytes(store.createSlice()); + store = DirectStore.allocate(length * size); + ((Byteable) ref).bytes(store.bytes(), 0L); recycle(ref); } private T createRef() { - T ref = valueGenerator.nativeInstance(tClass); - ((Byteable) ref).bytes(store.createSlice()); + T ref = DataValueClasses.newDirectReference(tClass); + ((Byteable) ref).bytes(store.bytes(), 0L); return ref; } @@ -65,13 +62,19 @@ public class HugeArrayImpl<T> implements HugeArray<T> { @Override public T get(long index) { T t = acquire(); - DirectBytes bytes = (DirectBytes) ((Byteable) t).bytes(); + Byteable byteable = (Byteable) t; + DirectBytes bytes = (DirectBytes) byteable.bytes(); bytes.positionAndSize(index * size, size); return t; } @Override public void get(long index, T element) { + if (tClass.isInstance(element)) { + DirectBytes bytes = (DirectBytes) ((Byteable) element).bytes(); + bytes.positionAndSize(index * size, size); + return; + } T t = acquire(); DirectBytes bytes = (DirectBytes) ((Byteable) t).bytes(); bytes.positionAndSize(index * size, size); diff --git a/lang/src/main/java/net/openhft/lang/collection/impl/HugeQueueImpl.java b/lang/src/main/java/net/openhft/lang/collection/impl/HugeQueueImpl.java index 620efc9..60b3fbc 100644 --- a/lang/src/main/java/net/openhft/lang/collection/impl/HugeQueueImpl.java +++ b/lang/src/main/java/net/openhft/lang/collection/impl/HugeQueueImpl.java @@ -1,17 +1,17 @@ /* - * Copyright 2013 Peter Lawrey + * Copyright (C) 2015 higherfrequencytrading.com * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as published by + * the Free Software Foundation, either version 3 of the License. * - * http://www.apache.org/licenses/LICENSE-2.0 + * 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 Lesser General Public License for more details. * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. + * You should have received a copy of the GNU Lesser General Public License + * along with this program. If not, see <http://www.gnu.org/licenses/>. */ package net.openhft.lang.collection.impl; diff --git a/lang/src/main/java/net/openhft/lang/io/AbstractBytes.java b/lang/src/main/java/net/openhft/lang/io/AbstractBytes.java index e167835..15a0279 100755 --- a/lang/src/main/java/net/openhft/lang/io/AbstractBytes.java +++ b/lang/src/main/java/net/openhft/lang/io/AbstractBytes.java @@ -1,66 +1,76 @@ /* - * Copyright 2013 Peter Lawrey + * Copyright (C) 2015 higherfrequencytrading.com * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as published by + * the Free Software Foundation, either version 3 of the License. * - * http://www.apache.org/licenses/LICENSE-2.0 + * 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 Lesser General Public License for more details. * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. + * You should have received a copy of the GNU Lesser General Public License + * along with this program. If not, see <http://www.gnu.org/licenses/>. */ package net.openhft.lang.io; import net.openhft.lang.Jvm; import net.openhft.lang.Maths; -import net.openhft.lang.io.serialization.BytesMarshallable; -import net.openhft.lang.io.serialization.BytesMarshaller; +import net.openhft.lang.io.serialization.BytesMarshallableSerializer; import net.openhft.lang.io.serialization.BytesMarshallerFactory; -import net.openhft.lang.io.serialization.CompactBytesMarshaller; -import net.openhft.lang.io.serialization.impl.NoMarshaller; +import net.openhft.lang.io.serialization.JDKZObjectSerializer; +import net.openhft.lang.io.serialization.ObjectSerializer; +import net.openhft.lang.io.serialization.impl.StringBuilderPool; import net.openhft.lang.io.serialization.impl.VanillaBytesMarshallerFactory; +import net.openhft.lang.io.view.BytesInputStream; +import net.openhft.lang.io.view.BytesOutputStream; +import net.openhft.lang.model.Byteable; +import net.openhft.lang.pool.EnumInterner; import net.openhft.lang.pool.StringInterner; import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.Nullable; import java.io.*; +import java.lang.reflect.Field; import java.math.BigInteger; +import java.nio.BufferUnderflowException; import java.nio.ByteBuffer; import java.nio.ByteOrder; import java.nio.charset.Charset; import java.text.SimpleDateFormat; import java.util.*; +import java.util.concurrent.atomic.AtomicInteger; import java.util.logging.Level; import java.util.logging.Logger; +import static java.lang.Long.numberOfTrailingZeros; + /** * @author peter.lawrey */ @SuppressWarnings("MagicNumber") public abstract class AbstractBytes implements Bytes { - public static final long BUSY_LOCK_LIMIT = 10L * 1000 * 1000 * 1000; - public static final int INT_LOCK_MASK = 0xFFFFFF; - public static final int UNSIGNED_BYTE_MASK = 0xFF; - public static final int UNSIGNED_SHORT_MASK = 0xFFFF; + public static final int END_OF_BUFFER = -1; public static final long UNSIGNED_INT_MASK = 0xFFFFFFFFL; + public static final int SLEEP_THRESHOLD = 20 * 1000 * 1000; + // todo add tests before using in ChronicleMap + static final int RW_LOCK_LIMIT = 30; + static final long RW_READ_LOCKED = 1L << 0; + static final long RW_WRITE_WAITING = 1L << RW_LOCK_LIMIT; + static final long RW_WRITE_LOCKED = 1L << 2 * RW_LOCK_LIMIT; + static final int RW_LOCK_MASK = (1 << RW_LOCK_LIMIT) - 1; + static final char[] HEXI_DECIMAL = "0123456789ABCDEF".toCharArray(); + private static final long BUSY_LOCK_LIMIT = 20L * 1000 * 1000 * 1000; + private static final int INT_LOCK_MASK; + private static final int UNSIGNED_BYTE_MASK = 0xFF; + private static final int UNSIGNED_SHORT_MASK = 0xFFFF; + private static final int USHORT_EXTENDED = UNSIGNED_SHORT_MASK; // extra 1 for decimal place. - static final int MAX_NUMBER_LENGTH = 1 + (int) Math.ceil(Math.log10(Long.MAX_VALUE)); - static final byte[] RADIX_PARSE = new byte[256]; - - static { - Arrays.fill(RADIX_PARSE, (byte) -1); - for (int i = 0; i < 10; i++) - RADIX_PARSE['0' + i] = (byte) i; - for (int i = 0; i < 26; i++) - RADIX_PARSE['A' + i] = RADIX_PARSE['a' + i] = (byte) (i + 10); - } - - private static final Logger LOGGER = Logger.getLogger(AbstractBytes.class.getName()); + private static final int MAX_NUMBER_LENGTH = 1 + (int) Math.ceil(Math.log10(Long.MAX_VALUE)); + private static final byte[] RADIX_PARSE = new byte[256]; + //private static final Logger LOGGER = Logger.getLogger(AbstractBytes.class.getName()); private static final Charset ISO_8859_1 = Charset.forName("ISO-8859-1"); private static final byte[] MIN_VALUE_TEXT = ("" + Long.MIN_VALUE).getBytes(); private static final byte[] Infinity = "Infinity".getBytes(); @@ -73,39 +83,49 @@ public abstract class AbstractBytes implements Bytes { private static final short SHORT_MIN_VALUE = Short.MIN_VALUE; private static final short SHORT_EXTENDED = Short.MIN_VALUE + 1; private static final short SHORT_MAX_VALUE = Short.MIN_VALUE + 2; - private static final int USHORT_EXTENDED = UNSIGNED_SHORT_MASK; // RandomDataInput private static final int INT_MIN_VALUE = Integer.MIN_VALUE; private static final int INT_EXTENDED = Integer.MIN_VALUE + 1; private static final int INT_MAX_VALUE = Integer.MIN_VALUE + 2; private static final long MAX_VALUE_DIVIDE_10 = Long.MAX_VALUE / 10; - private static final byte NULL = 'N'; - private static final byte ENUMED = 'E'; - private static final byte SERIALIZED = 'S'; private static final byte[] RADIX = "0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZ".getBytes(); - static boolean ID_LIMIT_WARNED = false; + private static final StringBuilderPool sbp = new StringBuilderPool(); + private static final ThreadLocal<DateCache> dateCacheTL = new ThreadLocal<DateCache>(); + private static final FastStringOperations STRING_OPS = createFastStringOperations(); + private static boolean ID_LIMIT_WARNED = false; + + static { + Arrays.fill(RADIX_PARSE, (byte) -1); + for (int i = 0; i < 10; i++) + RADIX_PARSE['0' + i] = (byte) i; + for (int i = 0; i < 26; i++) + RADIX_PARSE['A' + i] = RADIX_PARSE['a' + i] = (byte) (i + 10); + INT_LOCK_MASK = 0xFFFFFF; + } + + final AtomicInteger refCount; private final byte[] numberBuffer = new byte[MAX_NUMBER_LENGTH]; protected boolean finished; - protected BytesMarshallerFactory bytesMarshallerFactory; + volatile Thread singleThread = null; + private ObjectSerializer objectSerializer; private StringInterner stringInterner = null; - private BytesInputStream inputStream = null; - private BytesOutputStream outputStream = null; - private StringBuilder utfReader = null; - private SimpleDateFormat dateFormat = null; - private long lastDay = Long.MIN_VALUE; - @Nullable - private byte[] lastDateStr = null; + private boolean selfTerminating = false; - protected AbstractBytes() { - this(new VanillaBytesMarshallerFactory()); + AbstractBytes() { + this(new VanillaBytesMarshallerFactory(), new AtomicInteger(1)); } - protected AbstractBytes(BytesMarshallerFactory bytesMarshallerFactory) { + AbstractBytes(BytesMarshallerFactory bytesMarshallerFactory, AtomicInteger refCount) { + this(BytesMarshallableSerializer.create(bytesMarshallerFactory, JDKZObjectSerializer.INSTANCE), refCount); + } + + AbstractBytes(ObjectSerializer objectSerializer, AtomicInteger refCount) { this.finished = false; - this.bytesMarshallerFactory = bytesMarshallerFactory; + this.refCount = refCount; + setObjectSerializer(objectSerializer); } - static boolean equalsCaseIgnore(StringBuilder sb, String s) { + private static boolean equalsCaseIgnore(StringBuilder sb, String s) { if (sb.length() != s.length()) return false; for (int i = 0; i < s.length(); i++) @@ -171,21 +191,490 @@ public abstract class AbstractBytes implements Bytes { } private static void warnIdLimit(long id) { - LOGGER.log(Level.WARNING, "High thread id may result in collisions id: " + id); + LoggerHolder.LOGGER.log(Level.WARNING, "High thread id may result in collisions id: " + id); ID_LIMIT_WARNED = true; } - protected StringInterner stringInterner() { + static int returnOrThrowEndOfBuffer(boolean selfTerminating) { + if (selfTerminating) return END_OF_BUFFER; + throw new BufferUnderflowException(); + } + + public static void readUTF2(Bytes bytes, @NotNull Appendable appendable, int utflen, int count) throws IOException { + while (count < utflen) { + int c = bytes.readUnsignedByte(); + switch (c >> 4) { + case 0: + case 1: + case 2: + case 3: + case 4: + case 5: + case 6: + case 7: + /* 0xxxxxxx */ + count++; + appendable.append((char) c); + break; + + case 12: + case 13: { + /* 110x xxxx 10xx xxxx */ + count += 2; + if (count > utflen) + throw new UTFDataFormatException( + "malformed input: partial character at end"); + int char2 = bytes.readUnsignedByte(); + if ((char2 & 0xC0) != 0x80) + throw new UTFDataFormatException( + "malformed input around byte " + count + " was " + char2); + int c2 = (char) (((c & 0x1F) << 6) | + (char2 & 0x3F)); + appendable.append((char) c2); + break; + } + + case 14: { + /* 1110 xxxx 10xx xxxx 10xx xxxx */ + count += 3; + if (count > utflen) + throw new UTFDataFormatException( + "malformed input: partial character at end"); + int char2 = bytes.readUnsignedByte(); + int char3 = bytes.readUnsignedByte(); + + if (((char2 & 0xC0) != 0x80) || ((char3 & 0xC0) != 0x80)) + throw new UTFDataFormatException( + "malformed input around byte " + (count - 1) + " was " + char2 + " " + char3); + int c3 = (char) (((c & 0x0F) << 12) | + ((char2 & 0x3F) << 6) | + (char3 & 0x3F)); + appendable.append((char) c3); + break; + } + + default: + /* 10xx xxxx, 1111 xxxx */ + throw new UTFDataFormatException( + "malformed input around byte " + count); + } + } + } + + public static long findUTFLength(@NotNull CharSequence str) { + if (str instanceof String) + return findUTFLength((String) str); + int strlen = str.length(); + long utflen = strlen; + + for (int i = 0; i < strlen; i++) { + char c = str.charAt(i); + if ((c > 0x007F)) { + if (c > 0x07FF) { + utflen += 2; + + } else { + utflen += 1; + } + } + } + return utflen; + } + + public static long findUTFLength(@NotNull CharSequence str, int strlen) { + long utflen = 0L; + + for (int i = 0; i < strlen; ++i) { + long c = (long) str.charAt(i); + if (c >= 0L && c <= 127L) { + ++utflen; + } else if (c > 2047L) { + utflen += 3L; + } else { + utflen += 2L; + } + } + + return utflen; + } + + public static long findUTFLength(@NotNull String str) { + return STRING_OPS.getUtf8EncodedStringLength(str); + } + + private static FastStringOperations createFastStringOperations() { + try { + return new FastStringOperations16(); + } catch (Exception e) { + // do nothing + } + + try { + return new FastStringOperations17(); + } catch (Exception e) { + throw new AssertionError(e); + } + } + + public static void writeUTF0(Bytes bytes, @NotNull CharSequence str, int strlen) { + if (bytes instanceof DirectBytes) { + DirectBytes db = (DirectBytes) bytes; + if (str instanceof String) + writeUTF1(db, STRING_OPS.extractChars((String) str), strlen); + else + writeUTF1(db, str, strlen); + } else { + writeUTF1(bytes, str, strlen); + } + } + + static void writeUTF1(DirectBytes bytes, @NotNull CharSequence str, int strlen) { + int c; + int i; + for (i = 0; i < strlen; i++) { + c = str.charAt(i); + if (!((c >= 0x0000) && (c <= 0x007F))) +// if (c + Integer.MIN_VALUE - 1 <= Integer.MIN_VALUE + 0x007F-1) + break; + NativeBytes.UNSAFE.putByte(bytes.positionAddr + i, (byte) c); + } + bytes.skip(i); + if (i < strlen) + writeUTF2(bytes, str, strlen, i); + } + + static void writeUTF1(DirectBytes bytes, @NotNull char[] chars, int strlen) { + int c; + int i; + ascii: + { + for (i = 0; i < strlen; i++) { + c = chars[i]; + if (!((c >= 0x0000) && (c <= 0x007F))) + break ascii; + NativeBytes.UNSAFE.putByte(bytes.positionAddr + i, (byte) c); + } + bytes.skip(i); + return; + } + bytes.skip(i); + if (i < strlen) + writeUTF2(bytes, chars, strlen, i); + } + + public static void writeUTF1(Bytes bytes, @NotNull CharSequence str, int strlen) { + int c; + int i; + for (i = 0; i < strlen; i++) { + c = str.charAt(i); + if (!((c >= 0x0000) && (c <= 0x007F))) + break; + bytes.write(c); + } + if (i < strlen) + writeUTF2(bytes, str, strlen, i); + } + + private static void writeUTF2(Bytes bytes, CharSequence str, int strlen, int i) { + int c; + for (; i < strlen; i++) { + c = str.charAt(i); + writeUTFchar(bytes, c); + } + } + + private static void writeUTFchar(Bytes bytes, int c) { + if ((c >= 0x0000) && (c <= 0x007F)) { + bytes.write(c); + + } else if (c > 0x07FF) { + bytes.write((byte) (0xE0 | ((c >> 12) & 0x0F))); + bytes.write((byte) (0x80 | ((c >> 6) & 0x3F))); + bytes.write((byte) (0x80 | (c & 0x3F))); + + } else { + bytes.write((byte) (0xC0 | ((c >> 6) & 0x1F))); + bytes.write((byte) (0x80 | c & 0x3F)); + } + } + + private static void writeUTF2(Bytes bytes, char[] str, int strlen, int i) { + int c; + for (; i < strlen; i++) { + c = str[i]; + writeUTFchar(bytes, c); + } + } + + static void checkArrayOffs(int arrayLength, int off, int len) { + if ((len | off) < 0 | ((off + len) & 0xffffffffL) > arrayLength) + throw new IndexOutOfBoundsException(); + } + + /** + * display the hex data of {@link Bytes} from the position() to the limit() + * + * @param buffer the buffer you wish to toString() + * @return hex representation of the buffer, from example [0D ,OA, FF] + */ + public static String toHex(@NotNull final Bytes buffer) { + if (buffer.remaining() == 0) + return ""; + + final Bytes slice = buffer.slice(); + final StringBuilder builder = new StringBuilder("["); + + while (slice.remaining() > 0) { + final byte b = slice.readByte(); + builder.append(((char) b) + "(" + String.format("%02X ", b).trim() + ")"); + builder.append(","); + } + + // remove the last comma + builder.deleteCharAt(builder.length() - 1); + builder.append("]"); + return builder.toString(); + } + + /** + * display the buffer as a string + * + * @param buffer the buffer you wish to toString() + * @return hex representation of the buffer, from example [0D ,OA, FF] + */ + public static String toString(@NotNull final Bytes buffer) { + final Bytes slice = buffer.slice(); + final StringBuilder builder = new StringBuilder(""); + + while (slice.remaining() > 0) { + final byte b = slice.readByte(); + builder.append((char) b); + } + return builder.toString(); + } + + static int rwReadLocked(long lock) { + return (int) (lock & RW_LOCK_MASK); + } + + static int rwWriteWaiting(long lock) { + return (int) ((lock >>> RW_LOCK_LIMIT) & RW_LOCK_MASK); + } + + static int rwWriteLocked(long lock) { + return (int) (lock >>> (2 * RW_LOCK_LIMIT)); + } + + public static String toHexString(@NotNull final Bytes bytes, long offset, long len) throws BufferUnderflowException { + if (len == 0) + return ""; + + int width = 16; + int[] lastLine = new int[width]; + String sep = ""; + long position = bytes.position(); + long limit = bytes.limit(); + + try { + + bytes.limit(offset + len); + bytes.position(offset); + + final StringBuilder builder = new StringBuilder(); + long start = offset / width * width; + long end = (offset + len + width - 1) / width * width; + for (long i = start; i < end; i += width) { + // check for duplicate rows + if (i + width < end) { + boolean same = true; + + for (int j = 0; j < width && i + j < offset + len; j++) { + int ch = bytes.readUnsignedByte(i + j); + same &= (ch == lastLine[j]); + lastLine[j] = ch; + } + if (i > start && same) { + sep = "........\n"; + continue; + } + } + builder.append(sep); + sep = ""; + String str = Long.toHexString(i); + for (int j = str.length(); j < 8; j++) + builder.append('0'); + builder.append(str); + for (int j = 0; j < width; j++) { + if (j == width / 2) + builder.append(' '); + if (i + j < start || i + j >= offset + len) { + builder.append(" "); + + } else { + builder.append(' '); + int ch = bytes.readUnsignedByte(i + j); + builder.append(HEXI_DECIMAL[ch >> 4]); + builder.append(HEXI_DECIMAL[ch & 15]); + } + } + builder.append(' '); + for (int j = 0; j < width; j++) { + if (j == width / 2) + builder.append(' '); + if (i + j < start || i + j >= offset + len) { + builder.append(' '); + + } else { + int ch = bytes.readUnsignedByte(i + j); + if (ch < ' ' || ch > 126) + ch = '\u00B7'; + builder.append((char) ch); + } + } + builder.append("\n"); + } + return builder.toString(); + } finally { + bytes.limit(limit); + bytes.position(position); + } + } + + protected void setObjectSerializer(ObjectSerializer objectSerializer) { + this.objectSerializer = objectSerializer; + } + + /** + * clearing the volatile singleThread is a write barrier. + */ + @Override + public void clearThreadAssociation() { + singleThread = null; + } + + boolean checkSingleThread() { + Thread t = Thread.currentThread(); + if (singleThread != t) + setThreadOrThrowException(t); + return true; + } + + private void setThreadOrThrowException(Thread t) { + if (singleThread == null) + singleThread = t; + else + throw new IllegalStateException("Altered by thread " + singleThread + " and " + t); + } + + public void readUTF0(@NotNull Appendable appendable, int utflen) + throws IOException { + int count = 0; + + while (count < utflen) { + int c = readUnsignedByteOrThrow(); + if (c >= 128) { + position(position() - 1); + readUTF2(this, appendable, utflen, count); + break; + } + count++; + appendable.append((char) c); + } + } + + @Override + public boolean read8bitText(@NotNull StringBuilder stringBuilder) throws StreamCorruptedException { + long len = readStopBit(); + if (len < 1) { + stringBuilder.setLength(0); + if (len == -1) + return false; + if (len == 0) + return true; + throw new StreamCorruptedException("UTF length invalid " + len + " remaining: " + remaining()); + } + if (len > remaining() || len > Integer.MAX_VALUE) + throw new StreamCorruptedException("UTF length invalid " + len + " remaining: " + remaining()); + int ilen = (int) len; + for (int i = 0; i < ilen; i++) + stringBuilder.append((char) readUnsignedByte()); + return true; + } + + @Override + public void write8bitText(@Nullable CharSequence s) { + if (s == null) { + writeStopBit(-1); + return; + } + writeStopBit(s.length()); + for (int i = 0; i < s.length(); i++) + writeUnsignedByte(s.charAt(i)); + } + + @Override + public long size() { + return capacity(); + } + + @Override + public void free() { + throw new UnsupportedOperationException("Forcing a free() via Bytes is unsafe, try reserve() + release()"); + } + + @Override + public void reserve() { + if (refCount.get() < 1) throw new IllegalStateException(); + refCount.incrementAndGet(); + } + + @Override + public boolean release() { + if (refCount.get() < 1) throw new IllegalStateException(); + if (refCount.decrementAndGet() > 0) return false; + cleanup(); + return true; + } + + protected abstract void cleanup(); + + @Override + public int refCount() { + return refCount.get(); + } + + StringInterner stringInterner() { if (stringInterner == null) stringInterner = new StringInterner(8 * 1024); return stringInterner; } @Override + public void selfTerminating(boolean selfTerminating) { + this.selfTerminating = selfTerminating; + } + + // RandomDataOutput + + @Override + public boolean selfTerminating() { + return selfTerminating; + } + + @Override + public int readUnsignedByteOrThrow() throws BufferUnderflowException { + return readByteOrThrow(selfTerminating); + } + + public int readByteOrThrow(boolean selfTerminating) throws BufferUnderflowException { + return remaining() < 1 ? returnOrThrowEndOfBuffer(selfTerminating) : readUnsignedByte(); + } + + @Override public Boolean parseBoolean(@NotNull StopCharTester tester) { - StringBuilder sb = acquireUtfReader(); - parseUTF(sb, tester); + StringBuilder sb = acquireStringBuilder(); + parseUtf8(sb, tester); if (sb.length() == 0) return null; switch (sb.charAt(0)) { @@ -215,16 +704,18 @@ public abstract class AbstractBytes implements Bytes { } @Override + public void readFully(@NotNull char[] data) { + readFully(data, 0, data.length); + } + + @Override public int skipBytes(int n) { - long position = position(); - int n2 = (int) Math.min(n, capacity() - position); - position(position + n2); - return n2; + return (int) skip(n); } @Override public boolean readBoolean() { - return readByte() != 0; + return readByteOrThrow(false) != 0; } @Override @@ -255,11 +746,12 @@ public abstract class AbstractBytes implements Bytes { @NotNull @Override public String readLine() { - StringBuilder input = acquireUtfReader(); + StringBuilder input = acquireStringBuilder(); EOL: while (position() < capacity()) { - int c = readUnsignedByte(); + int c = readUnsignedByteOrThrow(); switch (c) { + case END_OF_BUFFER: case '\n': break EOL; case '\r': @@ -278,14 +770,17 @@ public abstract class AbstractBytes implements Bytes { @Nullable @Override public String readUTFΔ() { - if (readUTFΔ(acquireUtfReader())) + StringBuilder utfReader = acquireStringBuilder(); + if (readUTFΔ(utfReader)) return utfReader.length() == 0 ? "" : stringInterner().intern(utfReader); return null; } + // locking at it temporarily changes position. + // todo write a version without changing the position. @Nullable @Override - public String readUTFΔ(long offset) throws IllegalStateException { + public synchronized String readUTFΔ(long offset) throws IllegalStateException { long position = position(); try { position(offset); @@ -295,15 +790,9 @@ public abstract class AbstractBytes implements Bytes { } } - // RandomDataOutput - @NotNull - private StringBuilder acquireUtfReader() { - if (utfReader == null) - utfReader = new StringBuilder(); - else - utfReader.setLength(0); - return utfReader; + private StringBuilder acquireStringBuilder() { + return sbp.acquireStringBuilder(); } @Override @@ -319,93 +808,27 @@ public abstract class AbstractBytes implements Bytes { @SuppressWarnings("MagicNumber") private boolean appendUTF0(@NotNull Appendable appendable) throws IOException { long len = readStopBit(); - if (len < -1 || len > Integer.MAX_VALUE) - throw new StreamCorruptedException("UTF length invalid " + len); if (len == -1) return false; + else if (len == 0) + return true; + if (len < -1 || len > remaining()) + throw new StreamCorruptedException("UTF length invalid " + len + " remaining: " + remaining()); int utflen = (int) len; readUTF0(appendable, utflen); return true; } - private void readUTF0(@NotNull Appendable appendable, int utflen) throws IOException { - int count = 0; - while (count < utflen) { - int c = readByte(); - if (c < 0) { - position(position() - 1); - break; - } - count++; - appendable.append((char) c); - } - - while (count < utflen) { - int c = readUnsignedByte(); - switch (c >> 4) { - case 0: - case 1: - case 2: - case 3: - case 4: - case 5: - case 6: - case 7: - /* 0xxxxxxx */ - count++; - appendable.append((char) c); - break; - case 12: - case 13: { - /* 110x xxxx 10xx xxxx */ - count += 2; - if (count > utflen) - throw new UTFDataFormatException( - "malformed input: partial character at end"); - int char2 = readUnsignedByte(); - if ((char2 & 0xC0) != 0x80) - throw new UTFDataFormatException( - "malformed input around byte " + count); - int c2 = (char) (((c & 0x1F) << 6) | - (char2 & 0x3F)); - appendable.append((char) c2); - break; - } - case 14: { - /* 1110 xxxx 10xx xxxx 10xx xxxx */ - count += 3; - if (count > utflen) - throw new UTFDataFormatException( - "malformed input: partial character at end"); - int char2 = readUnsignedByte(); - int char3 = readUnsignedByte(); - - if (((char2 & 0xC0) != 0x80) || ((char3 & 0xC0) != 0x80)) - throw new UTFDataFormatException( - "malformed input around byte " + (count - 1)); - int c3 = (char) (((c & 0x0F) << 12) | - ((char2 & 0x3F) << 6) | - (char3 & 0x3F)); - appendable.append((char) c3); - break; - } - default: - /* 10xx xxxx, 1111 xxxx */ - throw new UTFDataFormatException( - "malformed input around byte " + count); - } - } - } - @NotNull @Override - public String parseUTF(@NotNull StopCharTester tester) { - parseUTF(acquireUtfReader(), tester); + public String parseUtf8(@NotNull StopCharTester tester) { + StringBuilder utfReader = acquireStringBuilder(); + parseUtf8(utfReader, tester); return stringInterner().intern(utfReader); } @Override - public void parseUTF(@NotNull StringBuilder builder, @NotNull StopCharTester tester) { + public void parseUtf8(@NotNull StringBuilder builder, @NotNull StopCharTester tester) { builder.setLength(0); try { readUTF0(builder, tester); @@ -415,19 +838,23 @@ public abstract class AbstractBytes implements Bytes { } private void readUTF0(@NotNull Appendable appendable, @NotNull StopCharTester tester) throws IOException { - while (remaining() > 0) { - int c = readByte(); - if (c < 0) { + while (true) { + int c = readUnsignedByteOrThrow(); + if (c >= 128) { position(position() - 1); + readUTF2(appendable, tester); break; } if (tester.isStopChar(c)) return; appendable.append((char) c); } + } - while (remaining() > 0) { - int c = readUnsignedByte(); + // used by Chronicle Map 3.x + public void readUTF2(@NotNull Appendable appendable, @NotNull StopCharTester tester) throws IOException { + while (true) { + int c = readUnsignedByteOrThrow(); switch (c >> 4) { case 0: case 1: @@ -442,6 +869,7 @@ public abstract class AbstractBytes implements Bytes { return; appendable.append((char) c); break; + case 12: case 13: { /* 110x xxxx 10xx xxxx */ @@ -456,9 +884,9 @@ public abstract class AbstractBytes implements Bytes { appendable.append((char) c2); break; } + case 14: { /* 1110 xxxx 10xx xxxx 10xx xxxx */ - int char2 = readUnsignedByte(); int char3 = readUnsignedByte(); @@ -473,6 +901,7 @@ public abstract class AbstractBytes implements Bytes { appendable.append((char) c3); break; } + default: /* 10xx xxxx, 1111 xxxx */ throw new UTFDataFormatException( @@ -491,7 +920,7 @@ public abstract class AbstractBytes implements Bytes { @Override public boolean skipTo(@NotNull StopCharTester tester) { while (remaining() > 0) { - int ch = readByte(); + int ch = readUnsignedByteOrThrow(); if (tester.isStopChar(ch)) return true; } @@ -503,7 +932,8 @@ public abstract class AbstractBytes implements Bytes { public String readUTF() { try { int len = readUnsignedShort(); - readUTF0(acquireUtfReader(), len); + StringBuilder utfReader = acquireStringBuilder(); + readUTF0(utfReader, len); return utfReader.length() == 0 ? "" : stringInterner().intern(utfReader); } catch (IOException unexpected) { throw new AssertionError(unexpected); @@ -622,16 +1052,47 @@ public abstract class AbstractBytes implements Bytes { } @Override + public long readIncompleteLong(long offset) { + long left = remaining(); + if (left >= 8) + return readLong(offset); + if (left == 4) + return readInt(offset); + long l = 0; + for (int i = 0, remaining = (int) left; i < remaining; i++) { + l |= (long) readUnsignedByte(offset + i) << (i * 8); + } + return l; + } + + @Override public long readStopBit() { - long l = 0, b; - int count = 0; + long l; + if ((l = readByte()) >= 0) + return l; + return readStopBit0(l); + } + + private long readStopBit0(long l) { + l &= 0x7FL; + long b; + int count = 7; while ((b = readByte()) < 0) { l |= (b & 0x7FL) << count; count += 7; } - if (b == 0 && count > 0) + if (b != 0) { + if (count > 56) + throw new IllegalStateException( + "Cannot read more than 9 stop bits of positive value"); + return l | (b << count); + + } else { + if (count > 63) + throw new IllegalStateException( + "Cannot read more than 10 stop bits of negative value"); return ~l; - return l | (b << count); + } } @Override @@ -644,7 +1105,12 @@ public abstract class AbstractBytes implements Bytes { @Override public void read(@NotNull ByteBuffer bb) { - int len = (int) Math.min(bb.remaining(), remaining()); + read(bb, bb.remaining()); + } + + @Override + public void read(@NotNull ByteBuffer bb, int length) { + int len = (int) Math.min(length, remaining()); if (bb.order() == byteOrder()) { while (len >= 8) { bb.putLong(readLong()); @@ -660,24 +1126,22 @@ public abstract class AbstractBytes implements Bytes { // // RandomOutputStream @Override public void write(@NotNull byte[] bytes) { - int length = bytes.length; - checkWrite(length); - write(bytes, 0, length); + write(bytes, 0, bytes.length); } - private void checkWrite(int length) { + private void checkWrite(long length) { if (length > remaining()) throw new IllegalStateException("Cannot write " + length + " only " + remaining() + " remaining"); } @Override public void writeBoolean(boolean v) { - write(v ? -1 : 0); + write(v ? 'Y' : 0); } @Override public void writeBoolean(long offset, boolean v) { - writeByte(offset, v ? -1 : 0); + writeByte(offset, v ? 'Y' : 0); } @Override @@ -689,50 +1153,58 @@ public abstract class AbstractBytes implements Bytes { @Override public void writeChars(@NotNull String s) { - int len = s.length(); + writeChars((CharSequence) s); + } + + @Override + public void writeChars(@NotNull CharSequence cs) { + int len = cs.length(); for (int i = 0; i < len; i++) - writeChar(s.charAt(i)); + writeChar(cs.charAt(i)); } @Override public void writeUTF(@NotNull String str) { - long strlen = str.length(); - long utflen = findUTFLength(str, strlen); + long utflen = findUTFLength(str); if (utflen > 65535) throw new IllegalStateException("String too long " + utflen + " when encoded, max: 65535"); writeUnsignedShort((int) utflen); - writeUTF0(str, strlen); + checkUFTLength(utflen); + writeUTF0(this, str, str.length()); } @Override - public void writeUTFΔ(@Nullable CharSequence str) { + public void writeUTFΔ(@Nullable CharSequence str) throws IllegalArgumentException { if (str == null) { writeStopBit(-1); return; } - long strlen = str.length(); - long utflen = findUTFLength(str, strlen); + long utflen = findUTFLength(str); writeStopBit(utflen); - writeUTF0(str, strlen); + checkUFTLength(utflen); + writeUTF0(this, str, str.length()); } + // locking at it temporarily changes position. + // todo write a version without changing the position. @Override - public void writeUTFΔ(long offset, int maxSize, @Nullable CharSequence s) throws IllegalStateException { + public synchronized void writeUTFΔ(long offset, int maxSize, @Nullable CharSequence s) throws IllegalStateException { assert maxSize > 1; - if (s == null) { - writeStopBit(-1); - return; - } - long strlen = s.length(); - long utflen = findUTFLength(s, strlen); - long totalSize = IOTools.stopBitLength(utflen) + utflen; - if (totalSize > maxSize) - throw new IllegalStateException("Attempted to write " + totalSize + " byte String, when only " + maxSize + " allowed"); long position = position(); try { position(offset); + if (s == null) { + writeStopBit(-1); + return; + } + long utflen = findUTFLength(s); + long totalSize = IOTools.stopBitLength(utflen) + utflen; + if (totalSize > maxSize) + throw new IllegalStateException("Attempted to write " + totalSize + " byte String, when only " + maxSize + " allowed"); + writeStopBit(utflen); - writeUTF0(s, strlen); + writeUTF0(this, s, s.length()); + zeroOut(position(), offset + maxSize); } finally { position(position); } @@ -740,56 +1212,15 @@ public abstract class AbstractBytes implements Bytes { @NotNull public ByteStringAppender append(@NotNull CharSequence str) { - if (str == null) - return this; - long strlen = str.length(); - writeUTF0(str, strlen); + int strlen = str.length(); + writeUTF0(this, str, strlen); return this; } - private long findUTFLength(@NotNull CharSequence str, long strlen) { - long utflen = 0, c;/* use charAt instead of copying String to char array */ - for (int i = 0; i < strlen; i++) { - c = str.charAt(i); - if ((c >= 0x0000) && (c <= 0x007F)) { - utflen++; - } else if (c > 0x07FF) { - utflen += 3; - } else { - utflen += 2; - } - } - + private void checkUFTLength(long utflen) throws IllegalArgumentException { if (utflen > remaining()) throw new IllegalArgumentException( "encoded string too long: " + utflen + " bytes, remaining=" + remaining()); - return utflen; - } - - private void writeUTF0(@NotNull CharSequence str, long strlen) { - int c; - int i; - for (i = 0; i < strlen; i++) { - c = str.charAt(i); - if (!((c >= 0x0000) && (c <= 0x007F))) - break; - write(c); - } - - for (; i < strlen; i++) { - c = str.charAt(i); - if ((c >= 0x0000) && (c <= 0x007F)) { - write(c); - - } else if (c > 0x07FF) { - write((byte) (0xE0 | ((c >> 12) & 0x0F))); - write((byte) (0x80 | ((c >> 6) & 0x3F))); - write((byte) (0x80 | (c & 0x3F))); - } else { - write((byte) (0xC0 | ((c >> 6) & 0x1F))); - write((byte) (0x80 | c & 0x3F)); - } - } } @Override @@ -815,14 +1246,49 @@ public abstract class AbstractBytes implements Bytes { } @Override + public void write(long offset, Bytes bytes) { + long length = bytes.remaining(); + checkWrite(length); + long i; + for (i = 0; i < length - 7; i += 8) + writeLong(offset + i, bytes.readLong()); + for (; i < length; i++) + writeByte(offset + i, bytes.readByte()); + } + + @Override public void write(byte[] bytes, int off, int len) { - checkWrite(bytes.length); + checkArrayOffs(bytes.length, off, len); + checkWrite(len); for (int i = 0; i < len; i++) write(bytes[off + i]); } @Override + public void write(long offset, byte[] bytes, int off, int len) { + checkArrayOffs(bytes.length, off, len); + checkWrite(len); + + for (int i = 0; i < len; i++) + writeByte(offset + i, bytes[off + i]); + } + + @Override + public void write(@NotNull char[] data) { + write(data, 0, data.length); + } + + @Override + public void write(@NotNull char[] data, int off, int len) { + checkArrayOffs(data.length, off, len); + checkWrite(len * 2); + + for (int i = 0; i < len; i++) + writeChar(data[off + i]); + } + + @Override public void writeUnsignedShort(int v) { writeShort(v); } @@ -841,6 +1307,7 @@ public abstract class AbstractBytes implements Bytes { case Short.MIN_VALUE: writeByte(BYTE_MIN_VALUE); break; + case Short.MAX_VALUE: writeByte(BYTE_MAX_VALUE); break; @@ -855,6 +1322,7 @@ public abstract class AbstractBytes implements Bytes { public void writeCompactUnsignedShort(int v) { if (v >= 0 && v < USHORT_EXTENDED) { writeByte(v); + } else { writeUnsignedShort(USHORT_EXTENDED); writeUnsignedShort(v); @@ -866,6 +1334,7 @@ public abstract class AbstractBytes implements Bytes { if (byteOrder() == ByteOrder.BIG_ENDIAN) { writeUnsignedByte(v >>> 16); writeUnsignedShort(v); + } else { writeUnsignedByte(v); writeUnsignedShort(v >>> 8); @@ -877,6 +1346,7 @@ public abstract class AbstractBytes implements Bytes { if (byteOrder() == ByteOrder.BIG_ENDIAN) { writeUnsignedByte(offset, v >>> 16); writeUnsignedShort(offset + 1, v); + } else { writeUnsignedByte(offset, v); writeUnsignedShort(offset + 1, v >>> 8); @@ -902,6 +1372,7 @@ public abstract class AbstractBytes implements Bytes { case Integer.MIN_VALUE: writeShort(SHORT_MIN_VALUE); break; + case Integer.MAX_VALUE: writeShort(SHORT_MAX_VALUE); break; @@ -916,6 +1387,7 @@ public abstract class AbstractBytes implements Bytes { public void writeCompactUnsignedInt(long v) { if (v >= 0 && v < USHORT_EXTENDED) { writeShort((int) v); + } else { writeShort(USHORT_EXTENDED); writeUnsignedInt(v); @@ -927,6 +1399,7 @@ public abstract class AbstractBytes implements Bytes { if (byteOrder() == ByteOrder.BIG_ENDIAN) { writeUnsignedShort((int) (v >>> 32)); writeUnsignedInt(v); + } else { writeUnsignedShort((int) v); writeUnsignedInt(v >>> 16); @@ -938,6 +1411,7 @@ public abstract class AbstractBytes implements Bytes { if (byteOrder() == ByteOrder.BIG_ENDIAN) { writeUnsignedShort(offset, (int) (v >>> 32)); writeUnsignedInt(offset + 2, v); + } else { writeUnsignedShort(offset, (int) v); writeUnsignedInt(offset + 2, v >>> 16); @@ -958,31 +1432,42 @@ public abstract class AbstractBytes implements Bytes { } else { writeInt(INT_EXTENDED); writeLong(v); - } } @Override public void writeStopBit(long n) { + if ((n & ~0x7F) == 0) { + write((int) (n & 0x7f)); + return; + } + if ((n & ~0x3FFF) == 0) { + write((int) ((n & 0x7f) | 0x80)); + write((int) (n >> 7)); + return; + } + writeStopBit0(n); + } + + private void writeStopBit0(long n) { boolean neg = false; if (n < 0) { neg = true; n = ~n; } - while (true) { - long n2 = n >>> 7; - if (n2 != 0) { - writeByte((byte) (0x80 | (n & 0x7F))); - n = n2; - } else { - if (neg) { - writeByte((byte) (0x80 | (n & 0x7F))); - writeByte(0); - } else { - writeByte((byte) (n & 0x7F)); - } - break; - } + + long n2; + while ((n2 = n >>> 7) != 0) { + write((byte) (0x80L | n)); + n = n2; + } + // final byte + if (!neg) { + write((byte) n); + + } else { + write((byte) (0x80L | n)); + write(0); } } @@ -991,6 +1476,7 @@ public abstract class AbstractBytes implements Bytes { float f = (float) v; if (f == v) { writeFloat(f); + } else { writeFloat(Float.NaN); writeDouble(v); @@ -1006,23 +1492,7 @@ public abstract class AbstractBytes implements Bytes { writeByte(bb.get()); } - @Deprecated - @Override - public void writeStartToPosition(@NotNull Bytes bb) { - final long position = bb.position(); - long offset = 0; - if (position > remaining()) - throw new IndexOutOfBoundsException("Trying to write " + position + " when only " + remaining() + " left"); - // TODO optimise this to use Unsafe copy memory - while (position - offset >= 8) { - writeLong(bb.readLong(offset)); - offset += 8; - } - while (position - offset >= 1) - writeByte(bb.readByte(offset++)); - } - - // // ByteStringAppender + // ByteStringAppender @NotNull @Override public ByteStringAppender append(@NotNull CharSequence s, int start, int end) { @@ -1105,18 +1575,19 @@ public abstract class AbstractBytes implements Bytes { @NotNull @Override public ByteStringAppender appendDateMillis(long timeInMS) { - if (dateFormat == null) { - dateFormat = new SimpleDateFormat("yyyy/MM/dd"); - dateFormat.setTimeZone(TimeZone.getTimeZone("GMT")); + DateCache dateCache = dateCacheTL.get(); + if (dateCache == null) { + dateCacheTL.set(dateCache = new DateCache()); } long date = timeInMS / 86400000; - if (lastDay != date) { - lastDateStr = dateFormat.format(new Date(timeInMS)).getBytes(ISO_8859_1); - lastDay = date; + if (dateCache.lastDay != date) { + dateCache.lastDateStr = dateCache.dateFormat.format(new Date(timeInMS)).getBytes(ISO_8859_1); + dateCache.lastDay = date; + } else { - assert lastDateStr != null; + assert dateCache.lastDateStr != null; } - write(lastDateStr); + write(dateCache.lastDateStr); return this; } @@ -1168,13 +1639,16 @@ public abstract class AbstractBytes implements Bytes { if (exp == 0 && mantissa == 0) { writeByte('0'); return this; + } else if (exp == 2047) { if (mantissa == 0) { write(Infinity); + } else { write(NaN); } return this; + } else if (exp > 0) { mantissa += 1L << 52; } @@ -1283,8 +1757,26 @@ public abstract class AbstractBytes implements Bytes { int exp = 0; boolean negative = false; int decimalPlaces = Integer.MIN_VALUE; + int ch = readUnsignedByteOrThrow(); + switch (ch) { + case 'N': + if (compareRest("aN")) + return Double.NaN; + skip(-1); + return Double.NaN; + case 'I': + if (compareRest("nfinity")) + return Double.POSITIVE_INFINITY; + skip(-1); + return Double.NaN; + case '-': + if (compareRest("Infinity")) + return Double.NEGATIVE_INFINITY; + negative = true; + ch = readUnsignedByteOrThrow(); + break; + } while (true) { - byte ch = readByte(); if (ch >= '0' && ch <= '9') { while (value >= MAX_VALUE_DIVIDE_10) { value >>>= 1; @@ -1292,18 +1784,31 @@ public abstract class AbstractBytes implements Bytes { } value = value * 10 + (ch - '0'); decimalPlaces++; - } else if (ch == '-') { - negative = true; + } else if (ch == '.') { decimalPlaces = 0; + } else { break; } + ch = readUnsignedByteOrThrow(); } return asDouble(value, exp, negative, decimalPlaces); } + protected boolean compareRest(String s) { + if (s.length() > remaining()) + return false; + long position = position(); + for (int i = 0; i < s.length(); i++) { + if (readUnsignedByte(position + i) != s.charAt(i)) + return false; + } + skip(s.length()); + return true; + } + @NotNull @Override public <E> ByteStringAppender append(@NotNull Iterable<E> list, @NotNull CharSequence separator) { @@ -1321,7 +1826,7 @@ public abstract class AbstractBytes implements Bytes { } @NotNull - public <E> ByteStringAppender append(@NotNull List<E> list, @NotNull CharSequence separator) { + <E> ByteStringAppender append(@NotNull List<E> list, @NotNull CharSequence separator) { for (int i = 0; i < list.size(); i++) { if (i > 0) append(separator); @@ -1338,15 +1843,18 @@ public abstract class AbstractBytes implements Bytes { long num = 0, scale = Long.MIN_VALUE; boolean negative = false; while (true) { - byte b = readByte(); + int b = readUnsignedByteOrThrow(); // if (b >= '0' && b <= '9') if ((b - ('0' + Integer.MIN_VALUE)) <= 9 + Integer.MIN_VALUE) { num = num * 10 + b - '0'; scale++; + } else if (b == '.') { scale = 0; + } else if (b == '-') { negative = true; + } else { break; } @@ -1362,7 +1870,7 @@ public abstract class AbstractBytes implements Bytes { long num = 0; boolean negative = false; while (true) { - byte b = readByte(); + int b = readUnsignedByteOrThrow(); // if (b >= '0' && b <= '9') if ((b - ('0' + Integer.MIN_VALUE)) <= 9 + Integer.MIN_VALUE) num = num * 10 + b - '0'; @@ -1381,10 +1889,11 @@ public abstract class AbstractBytes implements Bytes { long num = 0; boolean negative = false; while (true) { - byte b = readByte(); + int b = readUnsignedByteOrThrow(); byte rp = RADIX_PARSE[b]; if (rp >= 0 && rp < base) { num = num * base + rp; + } else if (b == '-') negative = true; else @@ -1475,7 +1984,6 @@ public abstract class AbstractBytes implements Bytes { if (num <= 0) return 2; numberBuffer[1] = (byte) (num % 10L + '0'); - num /= 10; return 1; } @@ -1640,7 +2148,7 @@ public abstract class AbstractBytes implements Bytes { @NotNull @Override public ByteStringAppender append(@NotNull MutableDecimal md) { - StringBuilder sb = acquireUtfReader(); + StringBuilder sb = acquireStringBuilder(); md.toString(sb); append(sb); return this; @@ -1649,51 +2157,125 @@ public abstract class AbstractBytes implements Bytes { @NotNull @Override public InputStream inputStream() { - if (inputStream == null) - inputStream = new BytesInputStream(); - return inputStream; + return new BytesInputStream(this); } @NotNull @Override public OutputStream outputStream() { - if (outputStream == null) - outputStream = new BytesOutputStream(); - return outputStream; + return new BytesOutputStream(this); } @NotNull @Override - public BytesMarshallerFactory bytesMarshallerFactory() { - return bytesMarshallerFactory == null ? bytesMarshallerFactory = new VanillaBytesMarshallerFactory() : bytesMarshallerFactory; + public ObjectSerializer objectSerializer() { + return objectSerializer; } @SuppressWarnings("unchecked") @Override public <E> void writeEnum(@Nullable E e) { - Class aClass; - if (e == null || e instanceof CharSequence) - aClass = String.class; - else - aClass = (Class) e.getClass(); - BytesMarshaller<E> em = bytesMarshallerFactory().acquireMarshaller(aClass, true); - em.write(this, e); + if (e == null) { + write8bitText(null); + return; + } + if (e instanceof CharSequence) { + write8bitText((CharSequence) e); + return; + } + if (e instanceof Enum) { + write8bitText(e.toString()); + return; + } + + Class aClass = (Class) e.getClass(); + writeInstance(aClass, e); + } + + @Override + public void writeEnum(long offset, int len, Object e) { + long pos = position(); + long lim = limit(); + try { + position(offset); + limit(offset + len); + writeEnum(e); + } finally { + limit(lim); + position(pos); + } } @SuppressWarnings("unchecked") @Override public <E> E readEnum(@NotNull Class<E> eClass) { - BytesMarshaller<E> em = bytesMarshallerFactory().acquireMarshaller(eClass, true); - return em.read(this); + if (Enum.class.isAssignableFrom(eClass)) + return (E) readEnum2((Class<Enum>) (Class) eClass); + if (String.class.isAssignableFrom(eClass)) + return (E) readUTFΔ(); + return readInstance(eClass, null); + } + + @Override + public <E> E readEnum(long offset, int maxSize, Class<E> eClass) { + long pos = position(); + long lim = limit(); + try { + position(offset); + limit(offset + maxSize); + if (Enum.class.isAssignableFrom(eClass)) + return (E) readEnum2((Class<Enum>) (Class) eClass); + if (String.class.isAssignableFrom(eClass)) + return (E) readUTFΔ(); + return readInstance(eClass, null); + } finally { + limit(lim); + position(pos); + } + } + + @Override + public long nextSetBit(long fromIndex) { + if (fromIndex < 0) + throw new IndexOutOfBoundsException(); + long maxBit = capacity() << 3; + long fromLongIndex = fromIndex & ~63; + if (fromLongIndex >= maxBit) + return -1; + long firstByte = fromLongIndex >>> 3; + if ((fromIndex & 63) != 0) { + long l = readVolatileLong(firstByte) >>> fromIndex; + if (l != 0) { + return fromIndex + numberOfTrailingZeros(l); + } + firstByte += 8; + } + for (long i = firstByte; i < capacity(); i += 8) { + long l = readLong(i); + if (l != 0) + return (i << 3) + numberOfTrailingZeros(l); + } + return -1; + } + + private <E extends Enum<E>> E readEnum2(Class<E> eClass) { + try { + StringBuilder sb = acquireStringBuilder(); + if (read8bitText(sb)) + return EnumInterner.intern(eClass, sb); + return null; + } catch (StreamCorruptedException e) { + throw new IllegalStateException(e); + } } @SuppressWarnings("unchecked") @Override public <E extends Enum<E>> E parseEnum(@NotNull Class<E> eClass, @NotNull StopCharTester tester) { - String text = parseUTF(tester); + String text = parseUtf8(tester); if (text.isEmpty()) return null; - return Enum.valueOf(eClass, text); + return EnumInterner.intern(eClass, text); } @Override @@ -1714,11 +2296,13 @@ public abstract class AbstractBytes implements Bytes { @Override public <E> void readList(@NotNull Collection<E> list, @NotNull Class<E> eClass) { - int len = (int) readStopBit(); + long len = readStopBit(); + if (len < 0 || len > Integer.MAX_VALUE) + throw new IllegalStateException("Invalid length: " + len); list.clear(); for (int i = 0; i < len; i++) { @SuppressWarnings("unchecked") - E e = (E) readEnum(eClass); + E e = readEnum(eClass); list.add(e); } } @@ -1726,7 +2310,10 @@ public abstract class AbstractBytes implements Bytes { @Override @NotNull public <K, V> Map<K, V> readMap(@NotNull Map<K, V> map, @NotNull Class<K> kClass, @NotNull Class<V> vClass) { - int len = (int) readStopBit(); + long len = readStopBit(); + if (len < 0 || len > Integer.MAX_VALUE) + throw new IllegalStateException("Invalid length: " + len); + map.clear(); for (int i = 0; i < len; i++) map.put(readEnum(kClass), readEnum(vClass)); @@ -1740,7 +2327,7 @@ public abstract class AbstractBytes implements Bytes { @Override public int read() { - return remaining() > 0 ? readByte() : -1; + return remaining() > 0 ? readUnsignedByte() : -1; } @Override @@ -1753,11 +2340,11 @@ public abstract class AbstractBytes implements Bytes { @Override public long skip(long n) { - if (n < 0) + if (n < -position()) throw new IllegalArgumentException("Skip bytes out of range, was " + n); if (n > remaining()) n = remaining(); - skipBytes((int) n); + position(position() + n); return n; } @@ -1784,9 +2371,18 @@ public abstract class AbstractBytes implements Bytes { } @Override - public void reset() { + public AbstractBytes clear() { finished = false; position(0L); + limit(capacity()); + return this; + } + + @Override + public Bytes flip() { + limit(position()); + position(0); + return this; } @Override @@ -1797,26 +2393,10 @@ public abstract class AbstractBytes implements Bytes { @Nullable @Override public Object readObject() { - byte type = readByte(); - switch (type) { - case NULL: - return null; - case ENUMED: { - Class clazz = readEnum(Class.class); - return readEnum(clazz); - } - case SERIALIZED: { - try { - return new ObjectInputStream(this.inputStream()).readObject(); - } catch (Exception e) { - throw new IllegalStateException(e); - } - } - default: - BytesMarshaller<Object> m = bytesMarshallerFactory().getMarshaller(type); - if (m == null) - throw new IllegalStateException("Unknown type " + (char) type); - return m.read(this); + try { + return objectSerializer.readSerializable(this, null, null); + } catch (Exception e) { + throw new IllegalStateException(e); } } @@ -1829,82 +2409,83 @@ public abstract class AbstractBytes implements Bytes { throw new ClassCastException("Cannot convert " + o.getClass().getName() + " to " + tClass.getName() + " was " + o); } - @SuppressWarnings("unchecked") + @Nullable @Override - public void writeObject(@Nullable Object obj) { - if (obj == null) { - writeByte(NULL); - return; + @SuppressWarnings("unchecked") + public <T> T readInstance(@NotNull Class<T> objClass, T obj) { + try { + return objectSerializer.readSerializable(this, objClass, obj); + } catch (Exception e) { + throw new IllegalStateException(e); } + } - Class<?> clazz = obj.getClass(); - final BytesMarshallerFactory bytesMarshallerFactory = bytesMarshallerFactory(); - BytesMarshaller em = bytesMarshallerFactory.acquireMarshaller(clazz, false); - if (em == NoMarshaller.INSTANCE && autoGenerateMarshaller(obj)) - em = bytesMarshallerFactory.acquireMarshaller(clazz, true); - - if (em != NoMarshaller.INSTANCE) { - if (em instanceof CompactBytesMarshaller) { - writeByte(((CompactBytesMarshaller) em).code()); - em.write(this, obj); - return; - } - writeByte(ENUMED); - writeEnum(clazz); - em.write(this, obj); - return; - } - writeByte(SERIALIZED); - // TODO this is the lame implementation, but it works. + @SuppressWarnings("unchecked") + @Override + public void writeObject(@Nullable Object obj) { try { - ObjectOutputStream oos = new ObjectOutputStream(this.outputStream()); - oos.writeObject(obj); - } catch (IOException e) { + objectSerializer.writeSerializable(this, obj, null); + } catch (Exception e) { throw new IllegalStateException(e); } checkEndOfBuffer(); } - protected boolean autoGenerateMarshaller(Object obj) { - return (obj instanceof Comparable && obj.getClass().getPackage().getName().startsWith("java")) - || obj instanceof Externalizable - || obj instanceof BytesMarshallable; + @Override + public <OBJ> void writeInstance(@NotNull Class<OBJ> objClass, @NotNull OBJ obj) { + try { + objectSerializer.writeSerializable(this, obj, objClass); + } catch (Exception e) { + throw new IllegalStateException(e); + } + checkEndOfBuffer(); } @Override public boolean tryLockInt(long offset) { - long id = shortThreadId(); - return tryLockNanos4a(offset, (int) id); + return tryLockNanos4a(offset); } @Override public boolean tryLockNanosInt(long offset, long nanos) { - long id = shortThreadId(); int limit = nanos <= 10000 ? (int) nanos / 10 : 1000; for (int i = 0; i < limit; i++) - if (tryLockNanos4a(offset, (int) id)) + if (tryLockNanos4a(offset)) return true; if (nanos <= 10000) return false; long end = System.nanoTime() + nanos - 10000; do { - if (tryLockNanos4a(offset, (int) id)) + if (tryLockNanos4a(offset)) return true; } while (end > System.nanoTime() && !currentThread().isInterrupted()); return false; } - private boolean tryLockNanos4a(long offset, int id) { + private boolean tryLockNanos4a(long offset) { + //lowId = bottom 24 bytes of the thread id int lowId = shortThreadId(); + //Use the top 8 bytes as a counter, and the bottom 24 bytes as the thread id int firstValue = ((1 << 24) | lowId); + //If the cas works, it was unlocked and we now atomically have the lock if (compareAndSwapInt(offset, 0, firstValue)) return true; - long currentValue = readUnsignedInt(offset); + //The cas failed so get the value of the current lock + int currentValue = readInt(offset); + //if the bottom 24 bytes match our thread id ... + // TODO but what if we're in a different process? if ((currentValue & INT_LOCK_MASK) == lowId) { - if (currentValue >= (255L << 24)) - throw new IllegalStateException("Reentred 255 times without an unlock"); + //then if the counter in the top 8 bytes is 255, throw an exception + if ((currentValue >>> 24) >= 255) + throw new IllegalStateException("Reentered 255 times without an unlock - if you are using this to lock across processes, there could be a thread id conflict letting one process 'steal' the lock from another process. To avoid this, call AffinitySupport.setThreadId() during startup which will make all threads have unique ids"); + //otherwise increase the counter in the top 8 bytes by one currentValue += 1 << 24; - writeOrderedInt(offset, (int) currentValue); + //and store it - no other threads can successfully write at this point + //because their cas will fail (the value is not 0), so no update concurrency + //conflict, but we do want other threads to read the value we write + writeOrderedInt(offset, currentValue); + //we've got the lock - and incremented it, so return true + return true; } return false; } @@ -1925,18 +2506,25 @@ public abstract class AbstractBytes implements Bytes { int firstValue = ((1 << 24) | lowId); if (compareAndSwapInt(offset, firstValue, 0)) return; - // try to chek the lowId and the count. + // try to cheek the lowId and the count. unlockFailedInt(offset, lowId); } - private Thread currentThread; - private int shortThreadId = Integer.MIN_VALUE; + @Override + public void resetLockInt(long offset) { + writeOrderedInt(offset, 0); + } + + @Override + public int threadIdForLockInt(long offset) { + return readVolatileInt(offset) & INT_LOCK_MASK; + } - public int shortThreadId() { - return shortThreadId > 0 ? shortThreadId : shortThreadId0(); + int shortThreadId() { + return shortThreadId0(); } - protected int shortThreadId0() { + int shortThreadId0() { final int tid = (int) getId() & INT_LOCK_MASK; if (!ID_LIMIT_WARNED && tid > 1 << 24) { warnIdLimit(tid); @@ -1944,13 +2532,8 @@ public abstract class AbstractBytes implements Bytes { return tid; } - public void setCurrentThread() { - currentThread = Thread.currentThread(); - shortThreadId = shortThreadId0(); - } - - public Thread currentThread() { - return currentThread == null ? Thread.currentThread() : currentThread; + Thread currentThread() { + return Thread.currentThread(); } @Override @@ -1959,7 +2542,7 @@ public abstract class AbstractBytes implements Bytes { return tryLockNanos8a(offset, id); } - public long uniqueTid() { + long uniqueTid() { return Jvm.getUniqueTid(currentThread()); } @@ -1972,11 +2555,41 @@ public abstract class AbstractBytes implements Bytes { return true; if (nanos <= 10000) return false; - long end = System.nanoTime() + nanos - 10000; + return tryLockNanosLong0(offset, nanos, id); + } + + private boolean tryLockNanosLong0(long offset, long nanos, long id) { + long nanos0 = Math.min(nanos, SLEEP_THRESHOLD); + long start = System.nanoTime(); + long end0 = start + nanos0 - 10000; do { if (tryLockNanos8a(offset, id)) return true; - } while (end > System.nanoTime() && !currentThread().isInterrupted()); + } while (end0 > System.nanoTime() && !currentThread().isInterrupted()); + + long end = start + nanos - SLEEP_THRESHOLD; + if (LoggerHolder.LOGGER.isLoggable(Level.FINE)) { + LoggerHolder.LOGGER.log(Level.FINE, Thread.currentThread().getName() + ", waiting for lock"); + } + + try { + do { + if (tryLockNanos8a(offset, id)) { + long millis = (System.nanoTime() - start) / 1000000; + if (millis > 200) { + LoggerHolder.LOGGER.log(Level.WARNING, + Thread.currentThread().getName() + + ", to obtain a lock took " + + millis / 1e3 + " seconds" + ); + } + return true; + } + Thread.sleep(1); + } while (end > System.nanoTime()); + } catch (InterruptedException ignored) { + Thread.currentThread().interrupt(); + } return false; } @@ -1985,9 +2598,16 @@ public abstract class AbstractBytes implements Bytes { if (compareAndSwapLong(offset, 0, firstValue)) return true; long currentValue = readLong(offset); - if ((currentValue & (1L << 48) - 1) == id) { + long lockedId = currentValue & ((1L << 48) - 1); + if (lockedId == 0) { + int count = (int) (currentValue >>> 48); + if (count != 0) + LoggerHolder.LOGGER.log(Level.WARNING, "Lock held by threadId 0 !?"); + return compareAndSwapLong(offset, currentValue, firstValue); + } + if (lockedId == id) { if (currentValue >>> 48 == 65535) - throw new IllegalStateException("Reentred 65535 times without an unlock"); + throw new IllegalStateException("Reentered 65535 times without an unlock"); currentValue += 1L << 48; writeOrderedLong(offset, currentValue); return true; @@ -2015,18 +2635,30 @@ public abstract class AbstractBytes implements Bytes { unlockFailedLong(offset, id); } - protected long getId() { + @Override + public void resetLockLong(long offset) { + writeOrderedLong(offset, 0L); + } + + @Override + public long threadIdForLockLong(long offset) { + return readVolatileLong(offset); + } + + long getId() { return currentThread().getId(); } private void unlockFailedInt(long offset, int lowId) throws IllegalMonitorStateException { - long currentValue = readUnsignedInt(offset); + long currentValue = readInt(offset); long holderId = currentValue & INT_LOCK_MASK; if (holderId == lowId) { currentValue -= 1 << 24; writeOrderedInt(offset, (int) currentValue); + } else if (currentValue == 0) { - throw new IllegalMonitorStateException("No thread holds this lock"); + LoggerHolder.LOGGER.log(Level.WARNING, "No thread holds this lock, threadId: " + shortThreadId()); + } else { throw new IllegalMonitorStateException("Thread " + holderId + " holds this lock, " + (currentValue >>> 24) + " times"); } @@ -2038,8 +2670,10 @@ public abstract class AbstractBytes implements Bytes { if (holderId == id) { currentValue -= 1L << 48; writeOrderedLong(offset, currentValue); + } else if (currentValue == 0) { throw new IllegalMonitorStateException("No thread holds this lock"); + } else { throw new IllegalMonitorStateException("Process " + ((currentValue >>> 32) & 0xFFFF) + " thread " + (holderId & (-1L >>> 32)) @@ -2089,7 +2723,7 @@ public abstract class AbstractBytes implements Bytes { public short addShort(long offset, short s) { short s2 = readShort(offset); s2 += s; - writeByte(offset, s2); + writeShort(offset, s2); return s2; } @@ -2198,20 +2832,19 @@ public abstract class AbstractBytes implements Bytes { @Override public int length() { - return (int) Math.min(Integer.MAX_VALUE, remaining()); + if (position() == 0) + return (int) Math.min(limit(), Integer.MAX_VALUE); + else if (position() == limit() || limit() == capacity()) + return (int) Math.min(position(), Integer.MAX_VALUE); + else + throw new IllegalStateException(); } @Override public char charAt(int index) { - return (char) readUnsignedByte(index); - } - - @Override - public CharSequence subSequence(int start, int end) { - StringBuilder sb = new StringBuilder(end - start + 1); - for (int i = start; i < end; i++) - sb.append(charAt(i)); - return sb; + if (index < 0 || index >= length()) + throw new IndexOutOfBoundsException(); + return (char) readUnsignedByte(position() + index); } @Override @@ -2225,99 +2858,407 @@ public abstract class AbstractBytes implements Bytes { @Override public void writeMarshallable(@NotNull Bytes out) { out.write(this, position(), remaining()); + } + @Override + public void write(RandomDataInput bytes) { + long toWrite = bytes.remaining(); + write(bytes, bytes.position(), toWrite); + bytes.skip(toWrite); } @Override - public void write(BytesCommon bytes, long position, long length) { - if (length > bytes.remaining()) + public void write(RandomDataInput bytes, long position, long length) { + if (length > remaining()) throw new IllegalArgumentException("Attempt to write " + length + " bytes with " + remaining() + " remaining"); - RandomDataInput rdi = (RandomDataInput) bytes; if (bytes.byteOrder() == byteOrder()) { while (length >= 8) { - writeLong(rdi.readLong(position)); + writeLong(bytes.readLong(position)); position += 8; length -= 8; } } while (length >= 1) { - writeByte(rdi.readByte(position)); + writeByte(bytes.readByte(position)); position++; length--; } } - protected class BytesInputStream extends InputStream { - private long mark = 0; + @Override + public void write(@NotNull Byteable byteable) { + if (byteable.bytes() == null) { + throw new IllegalArgumentException("Attempt to write an unitialized Byteable object"); + } - @Override - public int available() throws IOException { - long remaining = remaining(); - return (int) Math.min(Integer.MAX_VALUE, remaining); + write(byteable.bytes(), byteable.offset(), byteable.maxSize()); + } + + @Override + public boolean startsWith(RandomDataInput input) { + return compare(position(), input, input.position(), input.remaining()); + } + + @Override + public boolean compare(long offset, RandomDataInput input, long inputOffset, long len) { + if (offset < 0 || inputOffset < 0 || len < 0) + throw new IndexOutOfBoundsException(); + if (offset + len < 0 || offset + len > capacity() || inputOffset + len < 0 || + inputOffset + len > input.capacity()) { + return false; + } + long i = 0L; + for (; i < len - 7L; i += 8L) { + if (readLong(offset + i) != input.readLong(inputOffset + i)) + return false; + } + if (i < len - 3L) { + if (readInt(offset + i) != input.readInt(inputOffset + i)) + return false; + i += 4L; + } + if (i < len - 1L) { + if (readChar(offset + i) != input.readChar(inputOffset + i)) + return false; + i += 2L; + } + if (i < len) { + if (readByte(offset + i) != input.readByte(inputOffset + i)) + return false; } + return true; + } - @Override - public void close() throws IOException { - finish(); + @NotNull + @Override + public String toString() { + long remaining = remaining(); + if (remaining < 0 || remaining > 1L << 48) + return "invalid remaining: " + remaining(); + if (remaining > 1 << 20) + remaining = 1 << 20; + char[] chars = new char[(int) remaining]; + long pos = position(); + for (int i = 0; i < remaining; i++) { + chars[i] = (char) readUnsignedByte(i + pos); } + return new String(chars); + } - @SuppressWarnings("NonSynchronizedMethodOverridesSynchronizedMethod") - @Override - public void mark(int readlimit) { - mark = position(); + @NotNull + @Override + public String toDebugString() { + return toDebugString(64); + } + + @NotNull + @Override + public String toDebugString(long limit) { + StringBuilder sb = new StringBuilder(200); + sb.append("[pos: ").append(position()).append(", lim: ").append(limit()).append(", cap: ") + .append(capacity()).append(" ] "); + toString(sb, position() - limit, position(), position() + limit); + + return sb.toString(); + } + + @Override + public String toHexString(long limit) { + return toHexString(this, position(), Math.min(remaining(), limit)); + } + + @Override + public void toString(Appendable sb, long start, long position, long end) { + try { + // before + if (start < 0) start = 0; + if (position > start) { + for (long i = start; i < position; i++) { + append(sb, i); + } + sb.append('\u2016'); + } + if (end > limit()) + end = limit(); + // after + for (long i = position; i < end; i++) { + append(sb, i); + } + } catch (IOException e) { + try { + sb.append(e.toString()); + } catch (IOException e1) { + throw new AssertionError(e); + } } + } - @Override - public boolean markSupported() { - return true; + @Override + public ByteBuffer sliceAsByteBuffer(@Nullable ByteBuffer toReuse) { + throw new UnsupportedOperationException(); + } + + private void append(Appendable sb, long i) throws IOException { + int b = readUnsignedByte(i); + if (b == 0) + sb.append('\u0660'); + else if (b < 21) + sb.append((char) (b + 0x2487)); + else + sb.append((char) b); + } + + @Override + public void asString(Appendable appendable) { + try { + for (long i = position(); i < limit(); i++) + append(appendable, i); + } catch (IOException e) { + throw new AssertionError(e); } + } - @Override - public int read(byte[] b, int off, int len) throws IOException { - return AbstractBytes.this.read(b, off, len); + @Override + public CharSequence asString() { + StringBuilder sb = new StringBuilder(); + asString(sb); + return sb; + } + + @Override + public boolean compareAndSwapDouble(long offset, double expected, double value) { + long exp = Double.doubleToRawLongBits(expected); + long val = Double.doubleToRawLongBits(value); + return compareAndSwapLong(offset, exp, val); + } + + public File file() { + return null; + } + + // read/write lock support. + // short path in a small method so it can be inlined. + public boolean tryRWReadLock(long offset, long timeOutNS) throws IllegalStateException, InterruptedException { + long lock = readVolatileLong(offset); + int writersWaiting = rwWriteWaiting(lock); + int writersLocked = rwWriteLocked(lock); + // readers wait for waiting writers + if (writersLocked <= 0 && writersWaiting <= 0) { + // increment readers locked. + int readersLocked = rwReadLocked(lock); + if (readersLocked >= RW_LOCK_MASK) + throw new IllegalStateException("readersLocked has reached a limit of " + readersLocked); + if (compareAndSwapLong(offset, lock, lock + RW_READ_LOCKED)) + return true; } + return tryRWReadLock0(offset, timeOutNS); + } - @SuppressWarnings("NonSynchronizedMethodOverridesSynchronizedMethod") - @Override - public void reset() throws IOException { - position(mark); + private boolean tryRWReadLock0(long offset, long timeOutNS) throws IllegalStateException, InterruptedException { + long end = System.nanoTime() + timeOutNS; + // wait for no write locks, nor waiting writes. + for (; ; ) { + long lock = readVolatileLong(offset); + int writersWaiting = rwWriteWaiting(lock); + int writersLocked = rwWriteLocked(lock); + if (writersLocked <= 0 && writersWaiting <= 0) { + // increment readers locked. + int readersLocked = rwReadLocked(lock); + if (readersLocked >= RW_LOCK_MASK) + throw new IllegalStateException("readersLocked has reached a limit of " + readersLocked); + // add to the readLock count and decrease the readWaiting count. + if (compareAndSwapLong(offset, lock, lock + RW_READ_LOCKED)) + return true; + } + if (System.nanoTime() > end) + return false; + + if (currentThread().isInterrupted()) + throw new InterruptedException("Unable to obtain lock, interrupted"); } + } - @Override - public long skip(long n) throws IOException { - if (n > Integer.MAX_VALUE) - throw new IOException("Skip too large"); - return skipBytes((int) n); + public boolean tryRWWriteLock(long offset, long timeOutNS) throws IllegalStateException, InterruptedException { + long lock = readVolatileLong(offset); + int readersLocked = rwReadLocked(lock); + int writersLocked = rwWriteLocked(lock); + // writers don't wait for waiting readers. + if (readersLocked <= 0 && writersLocked <= 0) { + if (compareAndSwapLong(offset, lock, lock + RW_WRITE_LOCKED)) + return true; } + return tryRWWriteLock0(offset, timeOutNS); + } - @Override - public int read() throws IOException { - if (remaining() > 0) - return readUnsignedByte(); - return -1; + private boolean tryRWWriteLock0(long offset, long timeOutNS) throws IllegalStateException, InterruptedException { + for (; ; ) { + long lock = readVolatileLong(offset); + int writersWaiting = rwWriteWaiting(lock); + if (writersWaiting >= RW_LOCK_MASK) + throw new IllegalStateException("writersWaiting has reached a limit of " + writersWaiting); + if (compareAndSwapLong(offset, lock, lock + RW_WRITE_WAITING)) + break; + } + long end = System.nanoTime() + timeOutNS; + // wait for no write locks. + for (; ; ) { + long lock = readVolatileLong(offset); + int readersLocked = rwReadLocked(lock); + int writersWaiting = rwWriteWaiting(lock); + int writersLocked = rwWriteLocked(lock); + if (readersLocked <= 0 && writersLocked <= 0) { + // increment readers locked. + if (writersWaiting <= 0) { + System.err.println("writersWaiting has underflowed"); + return false; + } + // add to the readLock count and decrease the readWaiting count. + if (compareAndSwapLong(offset, lock, lock + RW_WRITE_LOCKED - RW_WRITE_WAITING)) + return true; + } + boolean interrupted = currentThread().isInterrupted(); + if (interrupted || System.nanoTime() > end) { + // release waiting + for (; ; ) { + if (writersWaiting <= 0) + throw new IllegalStateException("writersWaiting has underflowed"); + if (compareAndSwapLong(offset, lock, lock - RW_WRITE_WAITING)) + break; + lock = readVolatileLong(offset); + writersWaiting = rwWriteWaiting(lock); + } + if (interrupted) + throw new InterruptedException("Unable to obtain lock, interrupted"); + return false; + } } } - protected class BytesOutputStream extends OutputStream { - @Override - public void close() throws IOException { - finish(); + public void unlockRWReadLock(long offset) { + for (; ; ) { + long lock = readVolatileLong(offset); + int readersLocked = rwReadLocked(lock); + if (readersLocked <= 0) + throw new IllegalMonitorStateException("readerLock underflow"); + if (compareAndSwapLong(offset, lock, lock - RW_READ_LOCKED)) + return; } + } - @Override - public void write(@NotNull byte[] b) throws IOException { - AbstractBytes.this.write(b); + public void unlockRWWriteLock(long offset) { + for (; ; ) { + long lock = readVolatileLong(offset); + int writersLocked = rwWriteLocked(lock); + if (writersLocked != 1) + throw new IllegalMonitorStateException("writersLock underflow " + writersLocked); + if (compareAndSwapLong(offset, lock, lock - RW_WRITE_LOCKED)) + return; + } + } + + String dumpRWLock(long offset) { + long lock = readVolatileLong(offset); + int readersLocked = rwReadLocked(lock); + int writersWaiting = rwWriteWaiting(lock); + int writersLocked = rwWriteLocked(lock); + return "writerLocked: " + writersLocked + + ", writersWaiting: " + writersWaiting + + ", readersLocked: " + readersLocked; + } + + static class DateCache { + final SimpleDateFormat dateFormat = new SimpleDateFormat("yyyy/MM/dd"); + private long lastDay = Long.MIN_VALUE; + @Nullable + private byte[] lastDateStr = null; + + DateCache() { + dateFormat.setTimeZone(TimeZone.getTimeZone("GMT")); + } + } + + static class LoggerHolder { + public static final Logger LOGGER = Logger.getLogger(AbstractBytes.class.getName()); + } + + abstract static class FastStringOperations { + abstract long getUtf8EncodedStringLength(@NotNull String string); + + final int getUtf8CharSize(char c) { + if ((c > 0x007F)) { + if (c > 0x07FF) { + return 3; + } else { + return 2; + } + } + return 1; + } + + abstract char[] extractChars(String string); + } + + private static class FastStringOperations17 extends FastStringOperations { + final Field valueField; + + private FastStringOperations17() throws SecurityException, NoSuchFieldException { + valueField = String.class.getDeclaredField("value"); + valueField.setAccessible(true); + } + + char[] extractChars(String string) { + try { + return (char[]) valueField.get(string); + } catch (IllegalAccessException e) { + throw new AssertionError(e); + } } @Override - public void write(byte[] b, int off, int len) throws IOException { - AbstractBytes.this.write(b, off, len); + long getUtf8EncodedStringLength(String string) { + char[] chars = extractChars(string); + long utflen = chars.length; + for (char c : chars) { + if ((c > 0x007F)) { + if (c > 0x07FF) { + utflen += 2; + } else { + utflen++; + } + } + } + return utflen; + } + } + + private static final class FastStringOperations16 extends FastStringOperations17 { + private final Field offsetField, countField; + + private FastStringOperations16() throws SecurityException, NoSuchFieldException { + super(); + offsetField = String.class.getDeclaredField("offset"); + offsetField.setAccessible(true); + countField = String.class.getDeclaredField("count"); + countField.setAccessible(true); } @Override - public void write(int b) throws IOException { - checkWrite(1); - writeUnsignedByte(b); + long getUtf8EncodedStringLength(String string) { + final char[] chars = extractChars(string); + final int startIndex, endIndex; + + try { + startIndex = offsetField.getInt(string); + endIndex = startIndex + countField.getInt(string); + } catch (IllegalAccessException e) { + throw new AssertionError(e); + } + + long utflen = 0; + for (int i = startIndex; i < endIndex; ++i) { + utflen += getUtf8CharSize(chars[i]); + } + return utflen; } } } diff --git a/lang/src/main/java/net/openhft/lang/io/AbstractMappedStore.java b/lang/src/main/java/net/openhft/lang/io/AbstractMappedStore.java new file mode 100644 index 0000000..3708e1b --- /dev/null +++ b/lang/src/main/java/net/openhft/lang/io/AbstractMappedStore.java @@ -0,0 +1,269 @@ +/* + * Copyright (C) 2015 higherfrequencytrading.com + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as published by + * the Free Software Foundation, either version 3 of the License. + * + * 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 Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with this program. If not, see <http://www.gnu.org/licenses/>. + */ +package net.openhft.lang.io; + +import java.io.Closeable; +import java.io.File; +import java.io.IOException; +import java.io.RandomAccessFile; +import java.lang.reflect.InvocationTargetException; +import java.lang.reflect.Method; +import java.nio.channels.FileChannel; +import java.util.concurrent.atomic.AtomicInteger; +import java.util.logging.Level; +import java.util.logging.Logger; + +import net.openhft.lang.io.serialization.ObjectSerializer; +import net.openhft.lang.model.constraints.NotNull; +import sun.misc.Cleaner; +import sun.nio.ch.FileChannelImpl; + +abstract class AbstractMappedStore implements BytesStore, Closeable { + private static final int MAP_RO = 0; + private static final int MAP_RW = 1; + private static final int MAP_PV = 2; + + // retain to prevent GC. + private final File file; + private final RandomAccessFile raf; + private final Cleaner cleaner; + private final AtomicInteger refCount = new AtomicInteger(1); + private final FileChannel.MapMode mode; + protected final MmapInfoHolder mmapInfoHolder; + private ObjectSerializer objectSerializer; + + AbstractMappedStore(MmapInfoHolder mmapInfoHolder, File file, FileChannel.MapMode mode, + long startInFile, long size, ObjectSerializer objectSerializer) + throws IOException { + validateSize(size); + this.file = file; + this.mmapInfoHolder = mmapInfoHolder; + this.mmapInfoHolder.setSize(size); + this.objectSerializer = objectSerializer; + this.mode = mode; + + try { + this.raf = new RandomAccessFile(file, accesModeFor(mode)); + resizeIfNeeded(startInFile, size); + map(startInFile); + this.cleaner = Cleaner.create(this, new Unmapper(mmapInfoHolder, raf)); + } catch (Exception e) { + throw wrap(e); + } + } + + protected static void validateSize(long size) { + if (size <= 0 || size > 128L << 40) { + throw new IllegalArgumentException("invalid size: " + size); + } + } + + protected final void resizeIfNeeded(long startInFile, long newSize) throws IOException { + if (file.getAbsolutePath().startsWith("/dev/")) { + return; + } + if (startInFile > 0) { + if (raf.length() >= startInFile + newSize) { + return; + } + } else if (startInFile == 0) { + if (raf.length() == newSize) { + return; + } + } else { + throw new IllegalArgumentException( + "Start offset in file needs to be positive: " + startInFile); + } + if (mode != FileChannel.MapMode.READ_WRITE) { + throw new IOException( + "Cannot resize file to " + newSize + " as mode is not READ_WRITE"); + } + + raf.setLength(startInFile + newSize); + } + + protected final void map(long startInFile) throws IOException { + try { + mmapInfoHolder.setAddress( + map0(raf.getChannel(), imodeFor(mode), startInFile, mmapInfoHolder.getSize())); + } catch (Exception e) { + throw wrap(e); + } + } + + protected final void unmapAndSyncToDisk() throws IOException { + unmap0(mmapInfoHolder.getAddress(), mmapInfoHolder.getSize()); + syncToDisk(); + } + + public final void syncToDisk() throws IOException { + raf.getChannel().force(true); + } + + private static long map0(FileChannel fileChannel, int imode, long start, long size) + throws NoSuchMethodException, IllegalAccessException, InvocationTargetException { + Method map0 = fileChannel.getClass().getDeclaredMethod( + "map0", int.class, long.class, long.class); + map0.setAccessible(true); + return (Long) map0.invoke(fileChannel, imode, start, size); + } + + private static void unmap0(long address, long size) throws IOException { + try { + Method unmap0 = FileChannelImpl.class.getDeclaredMethod( + "unmap0", long.class, long.class); + unmap0.setAccessible(true); + unmap0.invoke(null, address, size); + } catch (Exception e) { + throw wrap(e); + } + } + + private static IOException wrap(Throwable e) { + if (e instanceof InvocationTargetException) + e = e.getCause(); + if (e instanceof IOException) + return (IOException) e; + return new IOException(e); + } + + private static String accesModeFor(FileChannel.MapMode mode) { + return mode == FileChannel.MapMode.READ_WRITE ? "rw" : "r"; + } + + private static int imodeFor(FileChannel.MapMode mode) { + int imode = -1; + if (mode == FileChannel.MapMode.READ_ONLY) + imode = MAP_RO; + else if (mode == FileChannel.MapMode.READ_WRITE) + imode = MAP_RW; + else if (mode == FileChannel.MapMode.PRIVATE) + imode = MAP_PV; + assert (imode >= 0); + return imode; + } + + @Override + public final ObjectSerializer objectSerializer() { + return objectSerializer; + } + + @Override + public final long address() { + return mmapInfoHolder.getAddress(); + } + + @Override + public final long size() { + return mmapInfoHolder.getSize(); + } + + @Override + public final void free() { + cleaner.clean(); + } + + @Override + public final void close() { + free(); + } + + @NotNull + public final DirectBytes bytes() { + return new DirectBytes(this, refCount); + } + + @NotNull + public final DirectBytes bytes(long offset, long length) { + return new DirectBytes(this, refCount, offset, length); + } + + public final File file() { + return file; + } + + static final class MmapInfoHolder { + private long address, size; + private volatile boolean locked; + + private void checkLock() { + if (locked) { + throw new IllegalStateException(); + } + } + + void lock() { + this.locked = true; + } + + void setAddress(long address) { + checkLock(); + this.address = address; + } + + long getAddress() { + return address; + } + + void setSize(long size) { + checkLock(); + this.size = size; + } + + long getSize() { + return size; + } + } + + private static final class Unmapper implements Runnable { + private final MmapInfoHolder mmapInfoHolder; + private final RandomAccessFile raf; + /* + * This is not for synchronization (since calling this from multiple + * threads through .free / .close is an user error!) but rather to make + * sure that if an explicit cleanup was performed, the cleaner does not + * retry cleaning up the resources. + */ + private volatile boolean cleanedUp; + + Unmapper(MmapInfoHolder mmapInfo, RandomAccessFile raf) { + this.mmapInfoHolder = mmapInfo; + this.raf = raf; + } + + public void run() { + if (cleanedUp) { + return; + } + cleanedUp = true; + + try { + unmap0(mmapInfoHolder.getAddress(), mmapInfoHolder.getSize()); + raf.getChannel().force(true); + // this also closes the underlying channel as per the documentation + raf.close(); + } catch (IOException e) { + UnmapperLoggerHolder.LOGGER.log(Level.SEVERE, + "An exception has occurred while cleaning up a MappedStore instance: " + + e.getMessage(), e); + } + } + } + + private static final class UnmapperLoggerHolder { + private static final Logger LOGGER = Logger.getLogger(Unmapper.class.getName()); + } +} diff --git a/lang/src/main/java/net/openhft/lang/io/BoundsCheckingDirectBytes.java b/lang/src/main/java/net/openhft/lang/io/BoundsCheckingDirectBytes.java new file mode 100644 index 0000000..eaa7062 --- /dev/null +++ b/lang/src/main/java/net/openhft/lang/io/BoundsCheckingDirectBytes.java @@ -0,0 +1,43 @@ +/* + * Copyright (C) 2015 higherfrequencytrading.com + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as published by + * the Free Software Foundation, either version 3 of the License. + * + * 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 Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with this program. If not, see <http://www.gnu.org/licenses/>. + */ + +package net.openhft.lang.io; + +import net.openhft.lang.model.constraints.NotNull; + +import java.util.concurrent.atomic.AtomicInteger; + +/** + * Unlike {@link net.openhft.lang.io.NativeBytes}, always throw check bounds and exceptions on all write methods, + * including that write a single primitive, e. g. {@link #writeInt(int)}. {@code NativeBytes} throw exceptions only if + * Java assertions enabled. + */ +public class BoundsCheckingDirectBytes extends DirectBytes { + + public BoundsCheckingDirectBytes(@NotNull BytesStore store, AtomicInteger refCount) { + super(store, refCount); + } + + @Override + void positionChecks(long positionAddr) { + actualPositionChecks(positionAddr); + } + + @Override + void offsetChecks(long offset, long len) { + actualOffsetChecks(offset, len); + } +} diff --git a/lang/src/main/java/net/openhft/lang/io/BoundsCheckingNativeBytes.java b/lang/src/main/java/net/openhft/lang/io/BoundsCheckingNativeBytes.java new file mode 100644 index 0000000..1deef28 --- /dev/null +++ b/lang/src/main/java/net/openhft/lang/io/BoundsCheckingNativeBytes.java @@ -0,0 +1,53 @@ +/* + * Copyright (C) 2015 higherfrequencytrading.com + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as published by + * the Free Software Foundation, either version 3 of the License. + * + * 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 Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with this program. If not, see <http://www.gnu.org/licenses/>. + */ + +package net.openhft.lang.io; + +import net.openhft.lang.io.serialization.ObjectSerializer; + +import java.util.concurrent.atomic.AtomicInteger; + +/** + * Unlike {@link NativeBytes}, always throw check bounds and exceptions on + * all write methods, including that write a single primitive, e. g. {@link #writeInt(int)}. + * {@code NativeBytes} throw exceptions only if Java assertions enabled. + */ +public class BoundsCheckingNativeBytes extends NativeBytes { + + public BoundsCheckingNativeBytes(long startAddr, long capacityAddr) { + super(startAddr, capacityAddr); + } + + public BoundsCheckingNativeBytes(ObjectSerializer objectSerializer, + long startAddr, long capacityAddr, + AtomicInteger refCount) { + super(objectSerializer, startAddr, capacityAddr, refCount); + } + + public BoundsCheckingNativeBytes(NativeBytes bytes) { + super(bytes); + } + + @Override + void positionChecks(long positionAddr) { + actualPositionChecks(positionAddr); + } + + @Override + void offsetChecks(long offset, long len) { + actualOffsetChecks(offset, len); + } +} diff --git a/lang/src/main/java/net/openhft/lang/io/ByteBufferBytes.java b/lang/src/main/java/net/openhft/lang/io/ByteBufferBytes.java index 5b85e87..f23a8df 100755..100644 --- a/lang/src/main/java/net/openhft/lang/io/ByteBufferBytes.java +++ b/lang/src/main/java/net/openhft/lang/io/ByteBufferBytes.java @@ -1,65 +1,187 @@ /* - * Copyright 2013 Peter Lawrey + * Copyright (C) 2015 higherfrequencytrading.com * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as published by + * the Free Software Foundation, either version 3 of the License. * - * http://www.apache.org/licenses/LICENSE-2.0 + * 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 Lesser General Public License for more details. * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. + * You should have received a copy of the GNU Lesser General Public License + * along with this program. If not, see <http://www.gnu.org/licenses/>. */ package net.openhft.lang.io; -import org.jetbrains.annotations.NotNull; +import net.openhft.lang.io.serialization.BytesMarshallableSerializer; +import net.openhft.lang.io.serialization.JDKZObjectSerializer; +import net.openhft.lang.io.serialization.impl.VanillaBytesMarshallerFactory; +import net.openhft.lang.model.constraints.NotNull; import sun.nio.ch.DirectBuffer; import java.io.EOFException; import java.nio.ByteBuffer; import java.nio.ByteOrder; import java.util.concurrent.atomic.AtomicBoolean; +import java.util.concurrent.atomic.AtomicInteger; /** * @author peter.lawrey */ -public class ByteBufferBytes extends AbstractBytes { - protected final ByteBuffer buffer; - protected int start, position, limit; - protected AtomicBoolean barrier; - +public class ByteBufferBytes extends AbstractBytes implements IByteBufferBytes { + private final ByteBuffer buffer; + private final int start; + private final int capacity; + private int position; + private int limit; + private AtomicBoolean barrier; + + /** + * Use the ByteBufferBytes.wrap(ByteBuffer) as DirectByteBuffer more efficient + */ + @Deprecated public ByteBufferBytes(ByteBuffer buffer) { this(buffer, 0, buffer.capacity()); } - public ByteBufferBytes(ByteBuffer buffer, int start, int limit) { + /** + * Use the ByteBufferBytes.wrap(ByteBuffer) as DirectByteBuffer more efficient + * + * @param buffer the buffer to populate + * @param start start of buffer + * * @param capacity len of buffer + */ + @Deprecated + public ByteBufferBytes(ByteBuffer buffer, int start, int capacity) { + super(BytesMarshallableSerializer.create(new VanillaBytesMarshallerFactory(), JDKZObjectSerializer.INSTANCE), new AtomicInteger(1)); + // We should set order to native, because compare-and-swap operations + // end up with native ops. Bytes interfaces handles only native order. + buffer.order(ByteOrder.nativeOrder()); this.buffer = buffer; this.start = position = start; - this.limit = limit; + this.capacity = limit = (capacity + start); + } + + public static IByteBufferBytes wrap(ByteBuffer buffer) { + if (buffer instanceof DirectBuffer) { + return new DirectByteBufferBytes(buffer); + } + + return new ByteBufferBytes(buffer.slice()); + } + + public static IByteBufferBytes wrap(ByteBuffer buffer, int start, int capacity) { + if (buffer instanceof DirectBuffer) { + return new DirectByteBufferBytes(buffer, start, capacity); + } + + return new ByteBufferBytes(buffer.slice(), start, capacity); + } + + @Override + public ByteBufferBytes slice() { + return new ByteBufferBytes(buffer(), position, limit - position); + } + + @Override + public ByteBufferBytes slice(long offset, long length) { + long sliceStart = position + offset; + assert sliceStart >= start && sliceStart < capacity; + long sliceEnd = sliceStart + length; + assert sliceEnd > sliceStart && sliceEnd <= capacity; + return new ByteBufferBytes(buffer(), (int) sliceStart, (int) length); + } + + @Override + public CharSequence subSequence(int start, int end) { + int subStart = position + start; + if (subStart < position || subStart > limit) + throw new IndexOutOfBoundsException(); + int subEnd = position + end; + if (subEnd < subStart || subEnd > limit) + throw new IndexOutOfBoundsException(); + if (start == end) + return ""; + return new ByteBufferBytes(buffer(), subStart, end - start); + } + + @Override + public ByteBufferBytes bytes() { + return new ByteBufferBytes(buffer(), start, capacity - start); + } + + @Override + public ByteBufferBytes bytes(long offset, long length) { + long sliceStart = start + offset; + assert sliceStart >= start && sliceStart < capacity; + long sliceEnd = sliceStart + length; + assert sliceEnd > sliceStart && sliceEnd <= capacity; + return new ByteBufferBytes(buffer(), (int) sliceStart, (int) length); + } + + @Override + public long address() { + if (buffer instanceof DirectBuffer) { + long address = ((DirectBuffer) buffer).address(); + if (address == 0) + throw new IllegalStateException("This buffer has no address, is it empty?"); + return address; + } + throw new IllegalStateException("A heap ByteBuffer doesn't have a fixed address"); + } + + @Override + public Bytes zeroOut() { + clear(); + int i = start; + for (; i < capacity - 7; i++) + buffer.putLong(i, 0L); + for (; i < capacity; i++) + buffer.put(i, (byte) 0); + return this; + } + + @Override + public Bytes zeroOut(long start, long end) { + if (start < 0 || end > limit()) + throw new IllegalArgumentException("start: " + start + ", end: " + end); + if (start >= end) + return this; + int i = (int) (this.start + start); + int j = (int) (this.start + end); + for (; i < j - 7; i++) + buffer.putLong(i, 0L); + for (; i < j; i++) + buffer.put(i, (byte) 0); + return this; + } + + @Override + public Bytes zeroOut(long start, long end, boolean ifNotZero) { + // ByteBuffers are allocated in memory eagerly. + return zeroOut(start, end); } public ByteBuffer buffer() { return buffer; } - protected void readBarrier() { + void readBarrier() { if (barrier == null) barrier = new AtomicBoolean(); barrier.get(); } - protected void writeBarrier() { + void writeBarrier() { if (barrier == null) barrier = new AtomicBoolean(); barrier.lazySet(false); } @Override public int read(@NotNull byte[] bytes, int off, int len) { - if (len < 0 || off < 0 || off + len > bytes.length) - throw new IllegalArgumentException(); + checkArrayOffs(bytes.length, off, len); long left = remaining(); if (left <= 0) return -1; int len2 = (int) Math.min(left, len); @@ -70,7 +192,7 @@ public class ByteBufferBytes extends AbstractBytes { @Override public byte readByte() { - if (position < limit) + if (position < capacity) return buffer.get(position++); throw new IndexOutOfBoundsException(); } @@ -78,15 +200,14 @@ public class ByteBufferBytes extends AbstractBytes { @Override public byte readByte(long offset) { int pos = (int) (start + offset); - if (pos < limit) + if (pos < capacity) return buffer.get(pos); throw new IndexOutOfBoundsException(); } @Override public void readFully(@NotNull byte[] b, int off, int len) { - if (len < 0 || off < 0 || off + len > b.length) - throw new IllegalArgumentException(); + checkArrayOffs(b.length, off, len); long left = remaining(); if (left < len) throw new IllegalStateException(new EOFException()); @@ -95,8 +216,28 @@ public class ByteBufferBytes extends AbstractBytes { } @Override + public void readFully(@NotNull char[] data, int off, int len) { + checkArrayOffs(data.length, off, len); + long left = remaining(); + if (left < len * 2L) + throw new IllegalStateException(new EOFException()); + for (int i = 0; i < len; i++) + data[off + i] = readChar(); + } + + @Override + public void readFully(long offset, byte[] bytes, int off, int len) { + checkArrayOffs(bytes.length, off, len); + long left = remaining(); + if (left < len) + throw new IllegalStateException(new EOFException()); + for (int i = 0; i < len; i++) + bytes[off + i] = readByte(offset + i); + } + + @Override public short readShort() { - if (position + 2 <= limit) { + if (position + 2 <= capacity) { short s = buffer.getShort(position); position += 2; return s; @@ -107,14 +248,14 @@ public class ByteBufferBytes extends AbstractBytes { @Override public short readShort(long offset) { int pos = (int) (start + offset); - if (pos + 2 <= limit) + if (pos + 2 <= capacity) return buffer.getShort(pos); throw new IndexOutOfBoundsException(); } @Override public char readChar() { - if (position + 2 <= limit) { + if (position + 2 <= capacity) { char ch = buffer.getChar(position); position += 2; return ch; @@ -125,14 +266,14 @@ public class ByteBufferBytes extends AbstractBytes { @Override public char readChar(long offset) { int pos = (int) (start + offset); - if (pos + 2 <= limit) + if (pos + 2 <= capacity) return buffer.getChar(pos); throw new IndexOutOfBoundsException(); } @Override public int readInt() { - if (position + 4 <= limit) { + if (position + 4 <= capacity) { int i = buffer.getInt(position); position += 4; return i; @@ -143,7 +284,7 @@ public class ByteBufferBytes extends AbstractBytes { @Override public int readInt(long offset) { int pos = (int) (start + offset); - if (pos + 4 <= limit) + if (pos + 4 <= capacity) return buffer.getInt(pos); throw new IndexOutOfBoundsException(); } @@ -162,7 +303,7 @@ public class ByteBufferBytes extends AbstractBytes { @Override public long readLong() { - if (position + 8 <= limit) { + if (position + 8 <= capacity) { long l = buffer.getLong(position); position += 8; return l; @@ -173,7 +314,7 @@ public class ByteBufferBytes extends AbstractBytes { @Override public long readLong(long offset) { int pos = (int) (start + offset); - if (pos + 8 <= limit) + if (pos + 8 <= capacity) return buffer.getLong(pos); throw new IndexOutOfBoundsException(); } @@ -192,7 +333,7 @@ public class ByteBufferBytes extends AbstractBytes { @Override public float readFloat() { - if (position + 4 <= limit) { + if (position + 4 <= capacity) { float f = buffer.getFloat(position); position += 4; return f; @@ -203,14 +344,14 @@ public class ByteBufferBytes extends AbstractBytes { @Override public float readFloat(long offset) { int pos = (int) (start + offset); - if (pos + 4 <= limit) + if (pos + 4 <= capacity) return buffer.getFloat(pos); throw new IndexOutOfBoundsException(); } @Override public double readDouble() { - if (position + 8 <= limit) { + if (position + 8 <= capacity) { double d = buffer.getDouble(position); position += 8; return d; @@ -221,14 +362,14 @@ public class ByteBufferBytes extends AbstractBytes { @Override public double readDouble(long offset) { int pos = (int) (start + offset); - if (pos + 8 <= limit) + if (pos + 8 <= capacity) return buffer.getDouble(pos); throw new IndexOutOfBoundsException(); } @Override public void write(int b) { - if (position < limit) + if (position < capacity) buffer.put(position++, (byte) b); else throw new IndexOutOfBoundsException(); @@ -237,7 +378,7 @@ public class ByteBufferBytes extends AbstractBytes { @Override public void writeByte(long offset, int b) { int pos = (int) (start + offset); - if (pos < limit) + if (pos < capacity) buffer.put(pos, (byte) b); else throw new IndexOutOfBoundsException(); @@ -245,9 +386,10 @@ public class ByteBufferBytes extends AbstractBytes { @Override public void writeShort(int v) { - if (position + 2 <= limit) { + if (position + 2 <= capacity) { buffer.putShort(position, (short) v); position += 2; + } else { throw new IndexOutOfBoundsException(); } @@ -256,7 +398,7 @@ public class ByteBufferBytes extends AbstractBytes { @Override public void writeShort(long offset, int v) { int pos = (int) (start + offset); - if (pos + 2 <= limit) + if (pos + 2 <= capacity) buffer.putShort(pos, (short) v); else throw new IndexOutOfBoundsException(); @@ -264,9 +406,10 @@ public class ByteBufferBytes extends AbstractBytes { @Override public void writeChar(int v) { - if (position + 2 <= limit) { + if (position + 2 <= capacity) { buffer.putChar(position, (char) v); position += 2; + } else { throw new IndexOutOfBoundsException(); } @@ -275,7 +418,7 @@ public class ByteBufferBytes extends AbstractBytes { @Override public void writeChar(long offset, int v) { int pos = (int) (start + offset); - if (pos + 2 <= limit) + if (pos + 2 <= capacity) buffer.putChar(pos, (char) v); else throw new IndexOutOfBoundsException(); @@ -283,9 +426,10 @@ public class ByteBufferBytes extends AbstractBytes { @Override public void writeInt(int v) { - if (position + 4 <= limit) { + if (position + 4 <= capacity) { buffer.putInt(position, v); position += 4; + } else { throw new IndexOutOfBoundsException(); } @@ -294,7 +438,7 @@ public class ByteBufferBytes extends AbstractBytes { @Override public void writeInt(long offset, int v) { int pos = (int) (start + offset); - if (pos + 4 <= limit) + if (pos + 4 <= capacity) buffer.putInt(pos, v); else throw new IndexOutOfBoundsException(); @@ -321,9 +465,10 @@ public class ByteBufferBytes extends AbstractBytes { @Override public void writeLong(long v) { - if (position + 8 <= limit) { + if (position + 8 <= capacity) { buffer.putLong(position, v); position += 8; + } else { throw new IndexOutOfBoundsException(); } @@ -332,7 +477,7 @@ public class ByteBufferBytes extends AbstractBytes { @Override public void writeLong(long offset, long v) { int pos = (int) (start + offset); - if (pos + 8 <= limit) + if (pos + 8 <= capacity) buffer.putLong(pos, v); else throw new IndexOutOfBoundsException(); @@ -359,9 +504,10 @@ public class ByteBufferBytes extends AbstractBytes { @Override public void writeFloat(float v) { - if (position + 4 <= limit) { + if (position + 4 <= capacity) { buffer.putFloat(position, v); position += 4; + } else { throw new IndexOutOfBoundsException(); } @@ -370,7 +516,7 @@ public class ByteBufferBytes extends AbstractBytes { @Override public void writeFloat(long offset, float v) { int pos = (int) (start + offset); - if (pos + 4 <= limit) + if (pos + 4 <= capacity) buffer.putFloat(pos, v); else throw new IndexOutOfBoundsException(); @@ -378,9 +524,10 @@ public class ByteBufferBytes extends AbstractBytes { @Override public void writeDouble(double v) { - if (position + 8 <= limit) { + if (position + 8 <= capacity) { buffer.putDouble(position, v); position += 8; + } else { throw new IndexOutOfBoundsException(); } @@ -389,7 +536,7 @@ public class ByteBufferBytes extends AbstractBytes { @Override public void writeDouble(long offset, double v) { int pos = (int) (start + offset); - if (pos + 8 <= limit) + if (pos + 8 <= capacity) buffer.putDouble(pos, v); else throw new IndexOutOfBoundsException(); @@ -411,15 +558,19 @@ public class ByteBufferBytes extends AbstractBytes { } @Override - public void position(long position) { + public ByteBufferBytes position(long position) { if (start + position > Integer.MAX_VALUE) - throw new IndexOutOfBoundsException("Position to large"); + throw new IllegalArgumentException("Position to large"); + if (position < 0) + throw new IllegalArgumentException("Position to small"); + this.position = (int) (start + position); + return this; } @Override public long capacity() { - return limit - start; + return capacity - start; } @Override @@ -427,6 +578,17 @@ public class ByteBufferBytes extends AbstractBytes { return limit - position; } + @Override + public long limit() { + return limit - start; + } + + @Override + public ByteBufferBytes limit(long limit) { + this.limit = (int) (start + limit); + return this; + } + @NotNull @Override public ByteOrder byteOrder() { @@ -435,7 +597,25 @@ public class ByteBufferBytes extends AbstractBytes { @Override public void checkEndOfBuffer() throws IndexOutOfBoundsException { - if (position < start || position > limit) + if (position < start || position > limit()) { throw new IndexOutOfBoundsException(); + } + } + + @Override + protected void cleanup() { + IOTools.clean(buffer); + } + + @Override + public Bytes load() { + int pageSize = NativeBytes.UNSAFE.pageSize(); + for (int offset = start; offset < capacity; offset += pageSize) + buffer.get(offset); + return this; + } + + public void alignPositionAddr(int powerOf2) { + position = (position + powerOf2 - 1) & ~(powerOf2 - 1); } } diff --git a/lang/src/main/java/net/openhft/lang/io/ByteBufferReuse.java b/lang/src/main/java/net/openhft/lang/io/ByteBufferReuse.java new file mode 100644 index 0000000..e14c9e0 --- /dev/null +++ b/lang/src/main/java/net/openhft/lang/io/ByteBufferReuse.java @@ -0,0 +1,159 @@ +/* + * Copyright (C) 2015 higherfrequencytrading.com + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as published by + * the Free Software Foundation, either version 3 of the License. + * + * 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 Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with this program. If not, see <http://www.gnu.org/licenses/>. + */ + +package net.openhft.lang.io; + +import org.objectweb.asm.ClassWriter; +import org.objectweb.asm.Label; +import org.objectweb.asm.MethodVisitor; +import org.objectweb.asm.Opcodes; +import sun.misc.Unsafe; + +import java.nio.ByteBuffer; +import java.security.AccessController; +import java.security.PrivilegedAction; +import java.util.Arrays; + +interface ByteBufferReuse { + ByteBufferReuse INSTANCE = Inner.getReuse(); + + ByteBuffer reuse(long addr, int cap, Object att, ByteBuffer toReuse); + + class Inner extends Reuses implements Opcodes { + private static ByteBufferReuse getReuse() { + ClassWriter cw = new ClassWriter(0); + MethodVisitor mv; + + final String reuseImplClassName = "net/openhft/lang/io/ByteBufferReuseImpl"; + cw.visit(V1_6, ACC_PUBLIC + ACC_SUPER, reuseImplClassName, null, + "sun/reflect/MagicAccessorImpl", + new String[]{"net/openhft/lang/io/ByteBufferReuse"}); + + { + mv = cw.visitMethod(ACC_PUBLIC, "<init>", "()V", null, null); + mv.visitCode(); + mv.visitVarInsn(ALOAD, 0); + mv.visitMethodInsn(INVOKESPECIAL, "java/lang/Object", "<init>", "()V", false); + mv.visitInsn(RETURN); + mv.visitMaxs(1, 1); + mv.visitEnd(); + } + + String attachedBufferFieldName = getAttachedBufferFieldName(); + { + mv = cw.visitMethod(ACC_PUBLIC, "reuse", + "(JILjava/lang/Object;Ljava/nio/ByteBuffer;)Ljava/nio/ByteBuffer;", + null, null); + mv.visitCode(); + mv.visitVarInsn(ALOAD, 5); + String directByteBuffer = "java/nio/DirectByteBuffer"; + mv.visitTypeInsn(INSTANCEOF, directByteBuffer); + Label l0 = new Label(); + mv.visitJumpInsn(IFEQ, l0); + mv.visitVarInsn(ALOAD, 5); + mv.visitTypeInsn(CHECKCAST, directByteBuffer); + mv.visitVarInsn(ASTORE, 6); + mv.visitVarInsn(ALOAD, 6); + mv.visitFieldInsn(GETFIELD, directByteBuffer, attachedBufferFieldName, "Ljava/lang/Object;"); + String settableAtt = "net/openhft/lang/io/SettableAtt"; + mv.visitTypeInsn(INSTANCEOF, settableAtt); + mv.visitJumpInsn(IFEQ, l0); + mv.visitVarInsn(ALOAD, 6); + mv.visitVarInsn(LLOAD, 1); + mv.visitFieldInsn(PUTFIELD, directByteBuffer, "address", "J"); + mv.visitVarInsn(ALOAD, 6); + mv.visitInsn(ICONST_M1); + mv.visitFieldInsn(PUTFIELD, directByteBuffer, "mark", "I"); + mv.visitVarInsn(ALOAD, 6); + mv.visitInsn(ICONST_0); + mv.visitFieldInsn(PUTFIELD, directByteBuffer, "position", "I"); + mv.visitVarInsn(ALOAD, 6); + mv.visitVarInsn(ILOAD, 3); + mv.visitFieldInsn(PUTFIELD, directByteBuffer, "limit", "I"); + mv.visitVarInsn(ALOAD, 6); + mv.visitVarInsn(ILOAD, 3); + mv.visitFieldInsn(PUTFIELD, directByteBuffer, "capacity", "I"); + mv.visitVarInsn(ALOAD, 6); + mv.visitFieldInsn(GETFIELD, directByteBuffer, attachedBufferFieldName, "Ljava/lang/Object;"); + mv.visitTypeInsn(CHECKCAST, settableAtt); + mv.visitVarInsn(ALOAD, 4); + mv.visitFieldInsn(PUTFIELD, settableAtt, "att", "Ljava/lang/Object;"); + mv.visitVarInsn(ALOAD, 6); + mv.visitInsn(ARETURN); + mv.visitLabel(l0); + mv.visitFrame(Opcodes.F_SAME, 0, null, 0, null); + mv.visitTypeInsn(NEW, settableAtt); + mv.visitInsn(DUP); + mv.visitMethodInsn(INVOKESPECIAL, settableAtt, "<init>", "()V", false); + mv.visitVarInsn(ASTORE, 6); + mv.visitVarInsn(ALOAD, 6); + mv.visitVarInsn(ALOAD, 4); + mv.visitFieldInsn(PUTFIELD, settableAtt, "att", "Ljava/lang/Object;"); + mv.visitTypeInsn(NEW, directByteBuffer); + mv.visitInsn(DUP); + mv.visitVarInsn(LLOAD, 1); + mv.visitVarInsn(ILOAD, 3); + mv.visitVarInsn(ALOAD, 6); + mv.visitMethodInsn(INVOKESPECIAL, directByteBuffer, "<init>", + "(JILjava/lang/Object;)V", false); + mv.visitInsn(ARETURN); + mv.visitMaxs(6, 7); + mv.visitEnd(); + } + cw.visitEnd(); + + final byte[] impl = cw.toByteArray(); + + final Unsafe unsafe = NativeBytes.UNSAFE; + Class clazz = AccessController.doPrivileged(new PrivilegedAction<Class>() { + @Override + public Class run() { + ClassLoader cl = MAGIC_CLASS_LOADER; + return unsafe.defineClass(reuseImplClassName, impl, 0, impl.length, cl, null); + } + }); + try { + return (ByteBufferReuse) clazz.newInstance(); + } catch (InstantiationException e) { + throw new RuntimeException(e); + } catch (IllegalAccessException e) { + throw new RuntimeException(e); + } + } + + private static String getAttachedBufferFieldName() { + try { + Class<?> clazz = Class.forName("java.nio.DirectByteBuffer"); + String[] possibleFieldNames = new String[] { "att", + "viewedBuffer" }; + for (String possibleFieldName : possibleFieldNames) { + try { + clazz.getDeclaredField(possibleFieldName); + return possibleFieldName; + } catch (Exception e) { + continue; + } + } + + throw new RuntimeException( + "Failed to find any of the possible field names on DirectByteBuffer: " + + Arrays.toString(possibleFieldNames)); + } catch (Exception e) { + throw new RuntimeException(e); + } + } + } +} diff --git a/lang/src/main/java/net/openhft/lang/io/ByteStringAppender.java b/lang/src/main/java/net/openhft/lang/io/ByteStringAppender.java index fd36102..281ba9a 100755..100644 --- a/lang/src/main/java/net/openhft/lang/io/ByteStringAppender.java +++ b/lang/src/main/java/net/openhft/lang/io/ByteStringAppender.java @@ -1,23 +1,23 @@ /* - * Copyright 2013 Peter Lawrey + * Copyright (C) 2015 higherfrequencytrading.com * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as published by + * the Free Software Foundation, either version 3 of the License. * - * http://www.apache.org/licenses/LICENSE-2.0 + * 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 Lesser General Public License for more details. * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. + * You should have received a copy of the GNU Lesser General Public License + * along with this program. If not, see <http://www.gnu.org/licenses/>. */ package net.openhft.lang.io; -import org.jetbrains.annotations.NotNull; -import org.jetbrains.annotations.Nullable; +import net.openhft.lang.model.constraints.NotNull; +import net.openhft.lang.model.constraints.Nullable; /** * @author peter.lawrey diff --git a/lang/src/main/java/net/openhft/lang/io/ByteStringParser.java b/lang/src/main/java/net/openhft/lang/io/ByteStringParser.java index 10132cc..d80c3bf 100755 --- a/lang/src/main/java/net/openhft/lang/io/ByteStringParser.java +++ b/lang/src/main/java/net/openhft/lang/io/ByteStringParser.java @@ -1,69 +1,91 @@ /* - * Copyright 2013 Peter Lawrey + * Copyright (C) 2015 higherfrequencytrading.com * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as published by + * the Free Software Foundation, either version 3 of the License. * - * http://www.apache.org/licenses/LICENSE-2.0 + * 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 Lesser General Public License for more details. * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. + * You should have received a copy of the GNU Lesser General Public License + * along with this program. If not, see <http://www.gnu.org/licenses/>. */ package net.openhft.lang.io; -import org.jetbrains.annotations.NotNull; -import org.jetbrains.annotations.Nullable; +import net.openhft.lang.model.constraints.NotNull; +import net.openhft.lang.model.constraints.Nullable; + +import java.nio.BufferUnderflowException; /** * @author peter.lawrey */ public interface ByteStringParser extends BytesCommon { /** - * Return true or false, or null if it could not be detected as true or false. Case is not important - * <p/> - * false: f, false, n, no, 0 - * <p/> - * true: t, true, y, yes, 1 + * If set to true, the end of the Bytes will be the end of any consuming String, double or long. + * If false, incomplete reads will throw a BufferUnderflowException + + * @param selfTerminate if true, the end of the Buffer is silent. + */ + void selfTerminating(boolean selfTerminate); + + /** + * @return if this Bytes self terminates. + */ + boolean selfTerminating(); + + /** + * @return the next unsigned byte or -1 if selfTerminating and the end is reached. + * @throws BufferUnderflowException if the end is reached and selfTerminating is false. + */ + int readUnsignedByteOrThrow() throws BufferUnderflowException; + + /** + * Return true or false, or null if it could not be detected + * as true or false. Case is not important + * + * <p>false: f, false, n, no, 0 + * + * <p>true: t, true, y, yes, 1 * * @param tester to detect the end of the text. * @return true, false, or null if neither. */ - Boolean parseBoolean(@NotNull StopCharTester tester); + Boolean parseBoolean(@NotNull StopCharTester tester) throws BufferUnderflowException; /** * Populate a StringBuilder with the UTF encoded text until the end. * - * @param builder to clear and append to. + * @param builder to zeroOut and append to. * @param tester to detect when to stop. */ - void parseUTF(@NotNull StringBuilder builder, @NotNull StopCharTester tester); + void parseUtf8(@NotNull StringBuilder builder, @NotNull StopCharTester tester) throws BufferUnderflowException; @NotNull - String parseUTF(@NotNull StopCharTester tester); + String parseUtf8(@NotNull StopCharTester tester) throws BufferUnderflowException; @Nullable - <E extends Enum<E>> E parseEnum(@NotNull Class<E> eClass, @NotNull StopCharTester tester); + <E extends Enum<E>> E parseEnum(@NotNull Class<E> eClass, @NotNull StopCharTester tester) throws BufferUnderflowException; @NotNull - MutableDecimal parseDecimal(@NotNull MutableDecimal decimal); + MutableDecimal parseDecimal(@NotNull MutableDecimal decimal) throws BufferUnderflowException; /** * @return the next long, stopping at the first invalid character */ - long parseLong(); + long parseLong() throws BufferUnderflowException; /** * @param base to use. * @return the next long, stopping at the first invalid character */ - long parseLong(int base); + long parseLong(int base) throws BufferUnderflowException; - double parseDouble(); + double parseDouble() throws BufferUnderflowException; /** * Make sure we just read a stop character @@ -80,4 +102,18 @@ public interface ByteStringParser extends BytesCommon { * @return true if we stopped at a stop character, false if we ran out of data. */ boolean skipTo(@NotNull StopCharTester tester); + + /** + * Dump the contents of this Bytes as text in the Appendable. + * + * @param appendable to append to + */ + void asString(Appendable appendable); + + /** + * Dump the contents of Bytes as a CharSequence + * + * @return the CharSequence for these Bytes. + */ + CharSequence asString(); } diff --git a/lang/src/main/java/net/openhft/lang/io/Bytes.java b/lang/src/main/java/net/openhft/lang/io/Bytes.java index c520613..e9a21be 100755 --- a/lang/src/main/java/net/openhft/lang/io/Bytes.java +++ b/lang/src/main/java/net/openhft/lang/io/Bytes.java @@ -1,26 +1,33 @@ /* - * Copyright 2013 Peter Lawrey + * Copyright (C) 2015 higherfrequencytrading.com * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as published by + * the Free Software Foundation, either version 3 of the License. * - * http://www.apache.org/licenses/LICENSE-2.0 + * 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 Lesser General Public License for more details. * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. + * You should have received a copy of the GNU Lesser General Public License + * along with this program. If not, see <http://www.gnu.org/licenses/>. */ package net.openhft.lang.io; +import net.openhft.lang.ReferenceCounted; import net.openhft.lang.io.serialization.BytesMarshallable; /** * @author peter.lawrey */ -public interface Bytes extends RandomDataInput, RandomDataOutput, RandomDataUpdate, - ByteStringAppender, ByteStringParser, CharSequence, BytesMarshallable { +public interface Bytes extends RandomDataInput, RandomDataOutput, RandomDataUpdate, BytesStore, + ByteStringAppender, ByteStringParser, CharSequence, BytesMarshallable, ReferenceCounted { + + /** + * Needed if the buffer is created in one thread and used in another. + */ + void clearThreadAssociation(); + } diff --git a/lang/src/main/java/net/openhft/lang/io/BytesCommon.java b/lang/src/main/java/net/openhft/lang/io/BytesCommon.java index 0549f61..88eb529 100755 --- a/lang/src/main/java/net/openhft/lang/io/BytesCommon.java +++ b/lang/src/main/java/net/openhft/lang/io/BytesCommon.java @@ -1,26 +1,28 @@ /* - * Copyright 2013 Peter Lawrey + * Copyright (C) 2015 higherfrequencytrading.com * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as published by + * the Free Software Foundation, either version 3 of the License. * - * http://www.apache.org/licenses/LICENSE-2.0 + * 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 Lesser General Public License for more details. * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. + * You should have received a copy of the GNU Lesser General Public License + * along with this program. If not, see <http://www.gnu.org/licenses/>. */ package net.openhft.lang.io; -import net.openhft.lang.io.serialization.BytesMarshallerFactory; -import org.jetbrains.annotations.NotNull; +import net.openhft.lang.io.serialization.ObjectSerializer; +import net.openhft.lang.model.constraints.NotNull; +import org.jetbrains.annotations.Nullable; import java.io.InputStream; import java.io.OutputStream; +import java.nio.ByteBuffer; import java.nio.ByteOrder; /** @@ -28,14 +30,27 @@ import java.nio.ByteOrder; */ public interface BytesCommon { /** - * @return the offset read/written so far + * @return the offset read/written which must be >= limit() */ long position(); /** * @param position to skip to + * @return this bytes object back + * @throws java.lang.IllegalArgumentException if positions < 0 || position >= limit */ - void position(long position); + Bytes position(long position) throws IllegalArgumentException; + + /** + * @return the current limit which must be >= capacity() + */ + long limit(); + + /** + * @param limit the new limit which must be >= capacity() + * @return this bytes object back + */ + Bytes limit(long limit); /** * @return space available @@ -60,9 +75,46 @@ public interface BytesCommon { boolean isFinished(); /** - * Start again, unfinished, position() == 0 + * Clears this buffer. The position is set to zero, the limit is set to + * the capacity, and the mark is discarded. + * + * <p> Invoke this method before using a sequence of channel-read or + * <i>put</i> operations to fill this buffer. For example: + * <pre>{@code + * buf.clear(); // Prepare buffer for reading + * in.read(buf); // Read data + * }</pre> + * + * <p>This method does not actually erase the data in the buffer, but it + * is named as if it did because it will most often be used in situations + * in which that might as well be the case. </p> + * + * @return This buffer */ - void reset(); + Bytes clear(); + + /** + * Flips this buffer. The limit is set to the current position and then + * the position is set to zero. If the mark is defined then it is + * discarded. + * + * <p> After a sequence of channel-read or <i>put</i> operations, invoke + * this method to prepare for a sequence of channel-write or relative + * <i>get</i> operations. For example: + * <pre>{@code + * buf.put(magic); // Prepend header + * in.read(buf); // Read data into rest of buffer + * buf.flip(); // Flip buffer + * out.write(buf); // Write header + data to channel + * }</pre> + * + * <p> This method is often used in conjunction with the {@link + * java.nio.ByteBuffer#compact compact} method when transferring data from + * one place to another. </p> + * + * @return This buffer + */ + Bytes flip(); /** * @return Byte order for reading binary @@ -86,7 +138,7 @@ public interface BytesCommon { * @return the factory for marshallers. */ @NotNull - BytesMarshallerFactory bytesMarshallerFactory(); + ObjectSerializer objectSerializer(); /** * @throws IndexOutOfBoundsException if the bounds of the Bytes has been exceeded. @@ -94,11 +146,98 @@ public interface BytesCommon { void checkEndOfBuffer() throws IndexOutOfBoundsException; /** - * Copy from one Bytes to another, moves the position by length + * Access every page to ensure those pages are in memory. + * + * @return this. + */ + Bytes load(); + + /** + * Write a portion of the Bytes to an Appendable for printing. + * + * @param sb to append to + * @param start first byte + * @param position where to place a cursor or 0 = none. + * @param end last byte to append. + */ + void toString(Appendable sb, long start, long position, long end); + + /** + * Align the position address to a power of 2. * - * @param bytes to copy - * @param position to copy from - * @param length to copy + * @param alignment power of 2 to align to. */ - void write(BytesCommon bytes, long position, long length); + void alignPositionAddr(int alignment); + + /** + * Creates a new bytes whose content is a shared subsequence of this bytes' + * content. + * + * <p>The content of the new bytes will start at this bytes' current + * position. Changes to this bytes' content will be visible in the new bytes, + * and vice versa; the two bytes' position and limit values will be + * independent. + * + * <p>The new bytes' position will be zero, its capacity and its limit + * will be the number of bytes remaining in this bytes. + * + * <p>{@code slice()} is equivalent of {@code slice(0, remaining())}. + * + * @return the new bytes + * @see #slice(long, long) + */ + Bytes slice(); + + /** + * Returns a {@code ByteBuffer} whose content is a shared subsequence of this bytes' content. + * + * <p>The content of the returned {@code ByteBuffer} will start at this bytes' current + * position. Changes to this bytes' content will be visible in the returned {@code ByteBuffer}, + * and vice versa; this bytes' and the returned {@code ByteBuffer}'s position and limit values + * will be independent. + * + * <p>The returned {@code ByteBuffer}'s position will be zero, its capacity and its limit + * will be the number of bytes remaining in this bytes. + * + * <p>If this bytes object is able to reuse to given {@code toReuse} {@code ByteBuffer}, it will + * be reused and returned back from this method, otherwise a new {@code ByteBuffer} instance + * is created and returned. + * + * @param toReuse a {@code ByteBuffer} to reuse + * @return a {@code ByteBuffer} view of this {@code Bytes} + * @see #slice() + * @see ByteBuffer#slice() + */ + ByteBuffer sliceAsByteBuffer(@Nullable ByteBuffer toReuse); + + /** + * Creates a new bytes whose content is a shared subsequence of this bytes' + * content. + * + * <p>The content of the new bytes will start at this bytes' current + * {@link #position()}{@code + offset}. Changes to this bytes' content + * will be visible in the new bytes, and vice versa; the two bytes' + * position and limit values will be independent. + * + * <p>The new bytes' position will be zero, its capacity and its limit + * will be equal to {@code length}. + * + * <p>{@code offset} can be negative (if current bytes' position is positive) + * and {@code length} can run out of current bytes' limit, the restriction + * is that new bytes' should be within this bytes' absolute bounds. + * + * @param offset relative offset of the new bytes from the current bytes' + * position + * @param length capacity of the new bytes + * @return the new bytes + * @see #slice() + */ + Bytes slice(long offset, long length); + + @NotNull + String toDebugString(); + + String toDebugString(long limit); + + String toHexString(long limit); } diff --git a/lang/src/main/java/net/openhft/lang/io/BytesHasher.java b/lang/src/main/java/net/openhft/lang/io/BytesHasher.java new file mode 100644 index 0000000..cb9535f --- /dev/null +++ b/lang/src/main/java/net/openhft/lang/io/BytesHasher.java @@ -0,0 +1,37 @@ +/* + * Copyright (C) 2015 higherfrequencytrading.com + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as published by + * the Free Software Foundation, either version 3 of the License. + * + * 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 Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with this program. If not, see <http://www.gnu.org/licenses/>. + */ + +package net.openhft.lang.io; + +public interface BytesHasher { + /** + * Provide a 64-bit hash for the bytes in Bytes between the bytes.position() and bytes.limit(); + * + * @param bytes to hash + * @return 64-bit hash + */ + long hash(Bytes bytes); + + /** + * Provide a 64-bit hash for the bytes between offset and limit + * + * @param bytes to hash + * @param offset the start inclusive + * @param limit the end exclusive + * @return 64-bit hash. + */ + long hash(Bytes bytes, long offset, long limit); +} diff --git a/lang/src/main/java/net/openhft/lang/io/BytesStore.java b/lang/src/main/java/net/openhft/lang/io/BytesStore.java new file mode 100644 index 0000000..b6ec3a9 --- /dev/null +++ b/lang/src/main/java/net/openhft/lang/io/BytesStore.java @@ -0,0 +1,61 @@ +/* + * Copyright (C) 2015 higherfrequencytrading.com + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as published by + * the Free Software Foundation, either version 3 of the License. + * + * 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 Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with this program. If not, see <http://www.gnu.org/licenses/>. + */ + +package net.openhft.lang.io; + +import net.openhft.lang.io.serialization.ObjectSerializer; + +import java.io.File; + +public interface BytesStore { + /** + * Create a bytes whose content is the whole bytes store. Call of this + * method is equivalent to {@code bytes(0, size())} call. + * + * @return the new bytes + * @see #bytes(long, long) + */ + Bytes bytes(); + + /** + * Slice a {@code Bytes} object with start address of + * {@link #address() address}{@code + offset} and capacity of {@code length}. + * + * <p>If this {@code BytesStore} is {@code Bytes} itself rather than natural + * {@code BytesStore} object, this method will offset the new bytes from the + * bytes' start, not from bytes' position like + * {@link Bytes#slice(long, long)}. + * + * <p>{@code offset} should be non-negative, {@code length} should be positive, + * {@code offset + length} should be less or equal to {@link #size() size}. + * + * @param offset offset of the new bytes from the bytes store address + * @param length capacity and limit of the new bytes + * @return the sliced {@code Bytes} + * @see #bytes() + */ + Bytes bytes(long offset, long length); + + long address(); + + long size(); + + void free(); + + ObjectSerializer objectSerializer(); + + File file(); +} diff --git a/lang/src/main/java/net/openhft/lang/io/CharBufferReuse.java b/lang/src/main/java/net/openhft/lang/io/CharBufferReuse.java new file mode 100644 index 0000000..89ef031 --- /dev/null +++ b/lang/src/main/java/net/openhft/lang/io/CharBufferReuse.java @@ -0,0 +1,122 @@ +/* + * Copyright (C) 2015 higherfrequencytrading.com + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as published by + * the Free Software Foundation, either version 3 of the License. + * + * 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 Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with this program. If not, see <http://www.gnu.org/licenses/>. + */ + +package net.openhft.lang.io; + +import org.objectweb.asm.ClassWriter; +import org.objectweb.asm.Label; +import org.objectweb.asm.MethodVisitor; +import org.objectweb.asm.Opcodes; +import sun.misc.Unsafe; + +import java.nio.CharBuffer; +import java.security.AccessController; +import java.security.PrivilegedAction; + +interface CharBufferReuse { + CharBufferReuse INSTANCE = Inner.getReuse(); + + CharBuffer reuse(CharSequence cs, CharBuffer toReuse); + + class Inner extends Reuses implements Opcodes { + private static CharBufferReuse getReuse() { + ClassWriter cw = new ClassWriter(0); + MethodVisitor mv; + + final String reuseImplClassName = "net/openhft/lang/io/CharBufferReuseImpl"; + cw.visit(V1_6, ACC_PUBLIC + ACC_SUPER, reuseImplClassName, null, + "sun/reflect/MagicAccessorImpl", + new String[] {"net/openhft/lang/io/CharBufferReuse"}); + + { + mv = cw.visitMethod(ACC_PUBLIC, "<init>", "()V", null, null); + mv.visitCode(); + mv.visitVarInsn(ALOAD, 0); + mv.visitMethodInsn(INVOKESPECIAL, "java/lang/Object", "<init>", "()V", false); + mv.visitInsn(RETURN); + mv.visitMaxs(1, 1); + mv.visitEnd(); + } + { + mv = cw.visitMethod(ACC_PUBLIC, "reuse", + "(Ljava/lang/CharSequence;Ljava/nio/CharBuffer;)Ljava/nio/CharBuffer;", + null, null); + mv.visitCode(); + mv.visitVarInsn(ALOAD, 2); + String stringCharBuffer = "java/nio/StringCharBuffer"; + mv.visitTypeInsn(INSTANCEOF, stringCharBuffer); + Label l0 = new Label(); + mv.visitJumpInsn(IFEQ, l0); + mv.visitVarInsn(ALOAD, 2); + mv.visitTypeInsn(CHECKCAST, stringCharBuffer); + mv.visitVarInsn(ASTORE, 3); + mv.visitVarInsn(ALOAD, 3); + mv.visitInsn(ICONST_M1); + mv.visitFieldInsn(PUTFIELD, stringCharBuffer, "mark", "I"); + mv.visitVarInsn(ALOAD, 3); + mv.visitInsn(ICONST_0); + mv.visitFieldInsn(PUTFIELD, stringCharBuffer, "position", "I"); + mv.visitVarInsn(ALOAD, 1); + String charSequence = "java/lang/CharSequence"; + mv.visitMethodInsn(INVOKEINTERFACE, charSequence, "length", "()I", true); + mv.visitVarInsn(ISTORE, 4); + mv.visitVarInsn(ALOAD, 3); + mv.visitVarInsn(ILOAD, 4); + mv.visitFieldInsn(PUTFIELD, stringCharBuffer, "limit", "I"); + mv.visitVarInsn(ALOAD, 3); + mv.visitVarInsn(ILOAD, 4); + mv.visitFieldInsn(PUTFIELD, stringCharBuffer, "capacity", "I"); + mv.visitVarInsn(ALOAD, 3); + mv.visitVarInsn(ALOAD, 1); + mv.visitFieldInsn(PUTFIELD, stringCharBuffer, "str", "Ljava/lang/CharSequence;"); + mv.visitVarInsn(ALOAD, 3); + mv.visitInsn(ARETURN); + mv.visitLabel(l0); + mv.visitFrame(Opcodes.F_SAME, 0, null, 0, null); + mv.visitTypeInsn(NEW, stringCharBuffer); + mv.visitInsn(DUP); + mv.visitVarInsn(ALOAD, 1); + mv.visitInsn(ICONST_0); + mv.visitVarInsn(ALOAD, 1); + mv.visitMethodInsn(INVOKEINTERFACE, charSequence, "length", "()I", true); + mv.visitMethodInsn(INVOKESPECIAL, stringCharBuffer, "<init>", + "(Ljava/lang/CharSequence;II)V", false); + mv.visitInsn(ARETURN); + mv.visitMaxs(5, 5); + mv.visitEnd(); + } + cw.visitEnd(); + + final byte[] impl = cw.toByteArray(); + + final Unsafe unsafe = NativeBytes.UNSAFE; + Class clazz = AccessController.doPrivileged(new PrivilegedAction<Class>() { + @Override + public Class run() { + ClassLoader cl = MAGIC_CLASS_LOADER; + return unsafe.defineClass(reuseImplClassName, impl, 0, impl.length, cl, null); + } + }); + try { + return (CharBufferReuse) clazz.newInstance(); + } catch (InstantiationException e) { + throw new RuntimeException(e); + } catch (IllegalAccessException e) { + throw new RuntimeException(e); + } + } + } +} diff --git a/lang/src/main/java/net/openhft/lang/io/CharBuffers.java b/lang/src/main/java/net/openhft/lang/io/CharBuffers.java new file mode 100644 index 0000000..546aeb6 --- /dev/null +++ b/lang/src/main/java/net/openhft/lang/io/CharBuffers.java @@ -0,0 +1,28 @@ +/* + * Copyright (C) 2015 higherfrequencytrading.com + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as published by + * the Free Software Foundation, either version 3 of the License. + * + * 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 Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with this program. If not, see <http://www.gnu.org/licenses/>. + */ + +package net.openhft.lang.io; + +import java.nio.CharBuffer; + +public final class CharBuffers { + + public static CharBuffer wrap(CharSequence cs, CharBuffer toReuse) { + return CharBufferReuse.INSTANCE.reuse(cs, toReuse); + } + + private CharBuffers() {} +} diff --git a/lang/src/main/java/net/openhft/lang/io/CheckedBytes.java b/lang/src/main/java/net/openhft/lang/io/CheckedBytes.java new file mode 100755 index 0000000..31e7526 --- /dev/null +++ b/lang/src/main/java/net/openhft/lang/io/CheckedBytes.java @@ -0,0 +1,1313 @@ +/* + * Copyright (C) 2015 higherfrequencytrading.com + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as published by + * the Free Software Foundation, either version 3 of the License. + * + * 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 Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with this program. If not, see <http://www.gnu.org/licenses/>. + */ + +// +// Source code recreated from a .class file by IntelliJ IDEA +// (powered by Fernflower decompiler) +// + +package net.openhft.lang.io; + +import net.openhft.lang.io.serialization.ObjectSerializer; +import net.openhft.lang.model.Byteable; +import org.jetbrains.annotations.NotNull; +import org.jetbrains.annotations.Nullable; + +import java.io.File; +import java.io.InputStream; +import java.io.OutputStream; +import java.io.StreamCorruptedException; +import java.lang.management.ManagementFactory; +import java.lang.management.ThreadInfo; +import java.nio.BufferUnderflowException; +import java.nio.ByteBuffer; +import java.nio.ByteOrder; +import java.util.Collection; +import java.util.List; +import java.util.Map; + +public class CheckedBytes implements Bytes { + private final Bytes bytes; + volatile boolean isClosed = false; + + public CheckedBytes(Bytes bytes) { + this.checkNotClosed(); + this.bytes = bytes; + } + + public void clearThreadAssociation() { + this.checkNotClosed(); + this.bytes.clearThreadAssociation(); + } + + public long size() { + this.checkNotClosed(); + return this.bytes.size(); + } + + void checkNotClosed() { + if(this.isClosed) { + System.err.print("Thread " + Thread.currentThread().getName() + " performing processing " + "after free()"); + ThreadInfo[] threads = ManagementFactory.getThreadMXBean().dumpAllThreads(true, true); + ThreadInfo[] arr$ = threads; + int len$ = threads.length; + + for(int i$ = 0; i$ < len$; ++i$) { + ThreadInfo info = arr$[i$]; + System.out.print(info); + } + + System.exit(-1); + } + } + public void free() { + this.isClosed = true; + this.bytes.free(); + } + + public void reserve() { + this.checkNotClosed(); + this.bytes.reserve(); + } + + public boolean release() { + this.checkNotClosed(); + return this.bytes.release(); + } + + public int refCount() { + this.checkNotClosed(); + return this.bytes.refCount(); + } + + public void selfTerminating(boolean selfTerminating) { + this.checkNotClosed(); + this.bytes.selfTerminating(selfTerminating); + } + + public boolean selfTerminating() { + this.checkNotClosed(); + return this.bytes.selfTerminating(); + } + + public int readUnsignedByteOrThrow() throws BufferUnderflowException { + return this.bytes.readUnsignedByteOrThrow(); + } + + public void write(long offset, Bytes bytes) { + bytes.write(offset, bytes); + } + + public Boolean parseBoolean(@NotNull StopCharTester tester) { + this.checkNotClosed(); + return this.bytes.parseBoolean(tester); + } + + public void readFully(@NotNull byte[] bytes) { + this.checkNotClosed(); + this.bytes.readFully(bytes); + } + + public void readFully(@NotNull char[] data) { + this.checkNotClosed(); + this.bytes.readFully(data); + } + + public int skipBytes(int n) { + this.checkNotClosed(); + return this.bytes.skipBytes(n); + } + + public boolean readBoolean() { + this.checkNotClosed(); + return this.bytes.readBoolean(); + } + + public boolean readBoolean(long offset) { + this.checkNotClosed(); + return this.bytes.readBoolean(offset); + } + + public int readUnsignedByte() { + this.checkNotClosed(); + return this.bytes.readUnsignedByte(); + } + + public int readUnsignedByte(long offset) { + this.checkNotClosed(); + return this.bytes.readUnsignedByte(offset); + } + + public int readUnsignedShort() { + this.checkNotClosed(); + return this.bytes.readUnsignedShort(); + } + + public int readUnsignedShort(long offset) { + this.checkNotClosed(); + return this.bytes.readUnsignedShort(offset); + } + + @NotNull + public String readLine() { + this.checkNotClosed(); + return this.bytes.readLine(); + } + + @Nullable + public String readUTFΔ() { + this.checkNotClosed(); + return this.bytes.readUTFΔ(); + } + + @Nullable + public String readUTFΔ(long offset) throws IllegalStateException { + return this.bytes.readUTFΔ(offset); + } + + public boolean readUTFΔ(@NotNull StringBuilder stringBuilder) { + this.checkNotClosed(); + return this.bytes.readUTFΔ(stringBuilder); + } + + @Override + public boolean read8bitText(@NotNull StringBuilder stringBuilder) throws StreamCorruptedException { + return false; + } + + @NotNull + public String parseUtf8(@NotNull StopCharTester tester) { + this.checkNotClosed(); + return this.bytes.parseUtf8(tester); + } + + public void parseUtf8(@NotNull StringBuilder builder, @NotNull StopCharTester tester) { + this.checkNotClosed(); + this.bytes.parseUtf8(builder, tester); + } + + public boolean stepBackAndSkipTo(@NotNull StopCharTester tester) { + this.checkNotClosed(); + return this.bytes.stepBackAndSkipTo(tester); + } + + public boolean skipTo(@NotNull StopCharTester tester) { + this.checkNotClosed(); + return this.bytes.skipTo(tester); + } + + @NotNull + public String readUTF() { + this.checkNotClosed(); + return this.bytes.readUTF(); + } + + public short readCompactShort() { + this.checkNotClosed(); + return this.bytes.readCompactShort(); + } + + public int readCompactUnsignedShort() { + this.checkNotClosed(); + return this.bytes.readCompactUnsignedShort(); + } + + public int readInt24() { + this.checkNotClosed(); + return this.bytes.readInt24(); + } + + public int readInt24(long offset) { + this.checkNotClosed(); + return this.bytes.readInt24(offset); + } + + public long readUnsignedInt() { + this.checkNotClosed(); + return this.bytes.readUnsignedInt(); + } + + public long readUnsignedInt(long offset) { + this.checkNotClosed(); + return this.bytes.readUnsignedInt(offset); + } + + public int readCompactInt() { + this.checkNotClosed(); + return this.bytes.readCompactInt(); + } + + public long readCompactUnsignedInt() { + this.checkNotClosed(); + return this.bytes.readCompactUnsignedInt(); + } + + public long readInt48() { + this.checkNotClosed(); + return this.bytes.readInt48(); + } + + public long readInt48(long offset) { + this.checkNotClosed(); + return this.bytes.readInt48(offset); + } + + public long readCompactLong() { + this.checkNotClosed(); + return this.bytes.readCompactLong(); + } + + public long readStopBit() { + this.checkNotClosed(); + return this.bytes.readStopBit(); + } + + public double readCompactDouble() { + this.checkNotClosed(); + return this.bytes.readCompactDouble(); + } + + public void read(@NotNull ByteBuffer bb) { + this.checkNotClosed(); + this.bytes.read(bb); + } + + public void read(@NotNull ByteBuffer bb, int length) { + this.checkNotClosed(); + this.bytes.read(bb, length); + } + + public void write(@NotNull byte[] bytes) { + this.checkNotClosed(); + this.bytes.write(bytes); + } + + public void writeBoolean(boolean v) { + this.checkNotClosed(); + this.bytes.writeBoolean(v); + } + + public void writeBoolean(long offset, boolean v) { + this.checkNotClosed(); + this.bytes.writeBoolean(offset, v); + } + + public void writeBytes(@NotNull String s) { + this.checkNotClosed(); + this.bytes.writeBytes(s); + } + + public void writeChars(@NotNull String s) { + this.checkNotClosed(); + this.bytes.writeChars(s); + } + + public void writeChars(@NotNull CharSequence cs) { + this.checkNotClosed(); + this.bytes.writeChars(cs); + } + + public void writeUTF(@NotNull String str) { + this.checkNotClosed(); + this.bytes.writeUTF(str); + } + + public void writeUTFΔ(@Nullable CharSequence str) throws IllegalArgumentException { + this.bytes.writeUTFΔ(str); + } + + public void writeUTFΔ(long offset, int maxSize, @Nullable CharSequence s) throws IllegalStateException { + this.bytes.writeUTFΔ(offset, maxSize, s); + } + + @Override + public void write8bitText(@Nullable CharSequence s) { + + } + + @NotNull + public ByteStringAppender append(@NotNull CharSequence str) { + this.checkNotClosed(); + return this.bytes.append(str); + } + + public void writeByte(int v) { + this.checkNotClosed(); + this.bytes.writeByte(v); + } + + public void writeUnsignedByte(int v) { + this.checkNotClosed(); + this.bytes.writeUnsignedByte(v); + } + + public void writeUnsignedByte(long offset, int v) { + this.checkNotClosed(); + this.bytes.writeUnsignedByte(offset, v); + } + + public void write(@NotNull char[] data) { + this.checkNotClosed(); + this.bytes.write(data); + } + + public void write(@NotNull char[] data, int off, int len) { + this.checkNotClosed(); + this.bytes.write(data, off, len); + } + + public void writeUnsignedShort(int v) { + this.checkNotClosed(); + this.bytes.writeUnsignedShort(v); + } + + public void writeUnsignedShort(long offset, int v) { + this.checkNotClosed(); + this.bytes.writeUnsignedShort(offset, v); + } + + public void writeCompactShort(int v) { + this.checkNotClosed(); + this.bytes.writeCompactShort(v); + } + + public void writeCompactUnsignedShort(int v) { + this.checkNotClosed(); + this.bytes.writeCompactUnsignedShort(v); + } + + public void writeInt24(int v) { + this.checkNotClosed(); + this.bytes.writeInt24(v); + } + + public void writeInt24(long offset, int v) { + this.checkNotClosed(); + this.bytes.writeInt24(offset, v); + } + + public void writeUnsignedInt(long v) { + this.checkNotClosed(); + this.bytes.writeUnsignedInt(v); + } + + public void writeUnsignedInt(long offset, long v) { + this.checkNotClosed(); + this.bytes.writeUnsignedInt(offset, v); + } + + public void writeCompactInt(int v) { + this.checkNotClosed(); + this.bytes.writeCompactInt(v); + } + + public void writeCompactUnsignedInt(long v) { + this.checkNotClosed(); + this.bytes.writeCompactUnsignedInt(v); + } + + public void writeInt48(long v) { + this.checkNotClosed(); + this.bytes.writeInt48(v); + } + + public void writeInt48(long offset, long v) { + this.checkNotClosed(); + this.bytes.writeInt48(offset, v); + } + + public void writeCompactLong(long v) { + this.checkNotClosed(); + this.bytes.writeCompactLong(v); + } + + public void writeStopBit(long n) { + this.checkNotClosed(); + this.bytes.writeStopBit(n); + } + + public void writeCompactDouble(double v) { + this.checkNotClosed(); + this.bytes.writeCompactDouble(v); + } + + public void write(@NotNull ByteBuffer bb) { + this.checkNotClosed(); + this.bytes.write(bb); + } + + @NotNull + public ByteStringAppender append(@NotNull CharSequence s, int start, int end) { + this.checkNotClosed(); + return this.bytes.append(s, start, end); + } + + @NotNull + public ByteStringAppender append(@Nullable Enum value) { + this.checkNotClosed(); + return this.bytes.append(value); + } + + @NotNull + public ByteStringAppender append(boolean b) { + this.checkNotClosed(); + return this.bytes.append(b); + } + + @NotNull + public ByteStringAppender append(char c) { + this.checkNotClosed(); + return this.bytes.append(c); + } + + @NotNull + public ByteStringAppender append(int num) { + this.checkNotClosed(); + return this.bytes.append(num); + } + + @NotNull + public ByteStringAppender append(long num) { + this.checkNotClosed(); + return this.bytes.append(num); + } + + @NotNull + public ByteStringAppender append(long num, int base) { + this.checkNotClosed(); + return this.bytes.append(num, base); + } + + @NotNull + public ByteStringAppender appendDateMillis(long timeInMS) { + this.checkNotClosed(); + return this.bytes.appendDateMillis(timeInMS); + } + + @NotNull + public ByteStringAppender appendDateTimeMillis(long timeInMS) { + this.checkNotClosed(); + return this.bytes.appendDateTimeMillis(timeInMS); + } + + @NotNull + public ByteStringAppender appendTimeMillis(long timeInMS) { + this.checkNotClosed(); + return this.bytes.appendTimeMillis(timeInMS); + } + + @NotNull + public ByteStringAppender append(double d) { + this.checkNotClosed(); + return this.bytes.append(d); + } + + public double parseDouble() { + this.checkNotClosed(); + return this.bytes.parseDouble(); + } + + @NotNull + public <E> ByteStringAppender append(@NotNull Iterable<E> list, @NotNull CharSequence separator) { + this.checkNotClosed(); + return this.bytes.append(list, separator); + } + + @NotNull + <E> ByteStringAppender append(@NotNull List<E> list, @NotNull CharSequence separator) { + this.checkNotClosed(); + return this.bytes.append(list, separator); + } + + @NotNull + public MutableDecimal parseDecimal(@NotNull MutableDecimal decimal) { + this.checkNotClosed(); + return this.bytes.parseDecimal(decimal); + } + + public long parseLong() { + this.checkNotClosed(); + return this.bytes.parseLong(); + } + + public long parseLong(int base) { + this.checkNotClosed(); + return this.bytes.parseLong(base); + } + + @NotNull + public ByteStringAppender append(double d, int precision) { + this.checkNotClosed(); + return this.bytes.append(d, precision); + } + + @NotNull + public ByteStringAppender append(@NotNull MutableDecimal md) { + this.checkNotClosed(); + return this.bytes.append(md); + } + + @NotNull + public InputStream inputStream() { + this.checkNotClosed(); + return this.bytes.inputStream(); + } + + @NotNull + public OutputStream outputStream() { + this.checkNotClosed(); + return this.bytes.outputStream(); + } + + @NotNull + public ObjectSerializer objectSerializer() { + this.checkNotClosed(); + return this.bytes.objectSerializer(); + } + + public <E> void writeEnum(@Nullable E e) { + this.checkNotClosed(); + this.bytes.writeEnum(e); + } + + public <E> E readEnum(@NotNull Class<E> eClass) { + this.checkNotClosed(); + return this.bytes.readEnum(eClass); + } + + @Override + public <E> E readEnum(long offset, int maxSize, Class<E> eClass) { + this.checkNotClosed(); + return this.bytes.readEnum(offset, maxSize, eClass); + } + + @Override + public void writeEnum(long offset, int maxSize, Object object) { + this.checkNotClosed(); + this.bytes.writeEnum(offset, maxSize, object); + } + + public <E extends Enum<E>> E parseEnum(@NotNull Class<E> eClass, @NotNull StopCharTester tester) { + this.checkNotClosed(); + return this.bytes.parseEnum(eClass, tester); + } + + public <E> void writeList(@NotNull Collection<E> list) { + this.checkNotClosed(); + this.bytes.writeList(list); + } + + public <K, V> void writeMap(@NotNull Map<K, V> map) { + this.checkNotClosed(); + this.bytes.writeMap(map); + } + + public <E> void readList(@NotNull Collection<E> list, @NotNull Class<E> eClass) { + this.checkNotClosed(); + this.bytes.readList(list, eClass); + } + + @NotNull + public <K, V> Map<K, V> readMap(@NotNull Map<K, V> map, @NotNull Class<K> kClass, @NotNull Class<V> vClass) { + this.checkNotClosed(); + return this.bytes.readMap(map, kClass, vClass); + } + + public int available() { + this.checkNotClosed(); + return this.bytes.available(); + } + + public int read() { + this.checkNotClosed(); + return this.bytes.read(); + } + + public int read(@NotNull byte[] bytes) { + this.checkNotClosed(); + return this.bytes.read(bytes); + } + + public long skip(long n) { + this.checkNotClosed(); + return this.bytes.skip(n); + } + + public void close() { + this.checkNotClosed(); + this.bytes.close(); + } + + public void finish() throws IndexOutOfBoundsException { + this.bytes.finish(); + } + + public boolean isFinished() { + this.checkNotClosed(); + return this.bytes.isFinished(); + } + + public Bytes clear() { + this.checkNotClosed(); + return this.bytes.clear(); + } + + public Bytes flip() { + this.checkNotClosed(); + return this.bytes.flip(); + } + + public void flush() { + this.checkNotClosed(); + this.bytes.flush(); + } + + @Nullable + public Object readObject() { + this.checkNotClosed(); + return this.bytes.readObject(); + } + + @Nullable + public <T> T readObject(Class<T> tClass) throws IllegalStateException { + return this.bytes.readObject(tClass); + } + + @Nullable + public <T> T readInstance(@NotNull Class<T> objClass, T obj) { + this.checkNotClosed(); + return this.bytes.readInstance(objClass, obj); + } + + public void writeObject(@Nullable Object obj) { + this.checkNotClosed(); + this.bytes.writeObject(obj); + } + + public <OBJ> void writeInstance(@NotNull Class<OBJ> objClass, @NotNull OBJ obj) { + this.checkNotClosed(); + this.bytes.writeInstance(objClass, obj); + } + + public boolean tryLockInt(long offset) { + this.checkNotClosed(); + return this.bytes.tryLockInt(offset); + } + + public boolean tryLockNanosInt(long offset, long nanos) { + this.checkNotClosed(); + return this.bytes.tryLockNanosInt(offset, nanos); + } + + public void busyLockInt(long offset) throws InterruptedException, IllegalStateException { + this.bytes.busyLockInt(offset); + } + + public void unlockInt(long offset) throws IllegalMonitorStateException { + this.bytes.unlockInt(offset); + } + + public void resetLockInt(long offset) { + this.checkNotClosed(); + this.bytes.resetLockInt(offset); + } + + public int threadIdForLockInt(long offset) { + this.checkNotClosed(); + return this.bytes.threadIdForLockInt(offset); + } + + public boolean tryLockLong(long offset) { + this.checkNotClosed(); + return this.bytes.tryLockLong(offset); + } + + public boolean tryLockNanosLong(long offset, long nanos) { + this.checkNotClosed(); + return this.bytes.tryLockNanosLong(offset, nanos); + } + + public void busyLockLong(long offset) throws InterruptedException, IllegalStateException { + this.bytes.busyLockLong(offset); + } + + public void unlockLong(long offset) throws IllegalMonitorStateException { + this.bytes.unlockLong(offset); + } + + public void resetLockLong(long offset) { + this.checkNotClosed(); + this.bytes.resetLockLong(offset); + } + + public long threadIdForLockLong(long offset) { + this.checkNotClosed(); + return this.bytes.threadIdForLockLong(offset); + } + + public int getAndAdd(long offset, int delta) { + this.checkNotClosed(); + return this.bytes.getAndAdd(offset, delta); + } + + public int addAndGetInt(long offset, int delta) { + this.checkNotClosed(); + return this.bytes.addAndGetInt(offset, delta); + } + + public byte addByte(long offset, byte b) { + this.checkNotClosed(); + return this.bytes.addByte(offset, b); + } + + public int addUnsignedByte(long offset, int i) { + this.checkNotClosed(); + return this.bytes.addUnsignedByte(offset, i); + } + + public short addShort(long offset, short s) { + this.checkNotClosed(); + return this.bytes.addShort(offset, s); + } + + public int addUnsignedShort(long offset, int i) { + this.checkNotClosed(); + return this.bytes.addUnsignedShort(offset, i); + } + + public int addInt(long offset, int i) { + this.checkNotClosed(); + return this.bytes.addInt(offset, i); + } + + public long addUnsignedInt(long offset, long i) { + this.checkNotClosed(); + return this.bytes.addUnsignedInt(offset, i); + } + + public long addLong(long offset, long i) { + this.checkNotClosed(); + return this.bytes.addLong(offset, i); + } + + public float addFloat(long offset, float f) { + this.checkNotClosed(); + return this.bytes.addFloat(offset, f); + } + + public double addDouble(long offset, double d) { + this.checkNotClosed(); + return this.bytes.addDouble(offset, d); + } + + public int addAtomicInt(long offset, int i) { + this.checkNotClosed(); + return this.bytes.addAtomicInt(offset, i); + } + + public long addAtomicLong(long offset, long delta) { + this.checkNotClosed(); + return this.bytes.addAtomicLong(offset, delta); + } + + public float addAtomicFloat(long offset, float delta) { + this.checkNotClosed(); + return this.bytes.addAtomicFloat(offset, delta); + } + + public double addAtomicDouble(long offset, double delta) { + this.checkNotClosed(); + return this.bytes.addAtomicDouble(offset, delta); + } + + public float readVolatileFloat(long offset) { + this.checkNotClosed(); + return this.bytes.readVolatileFloat(offset); + } + + public double readVolatileDouble(long offset) { + this.checkNotClosed(); + return this.bytes.readVolatileDouble(offset); + } + + public void writeOrderedFloat(long offset, float v) { + this.checkNotClosed(); + this.bytes.writeOrderedFloat(offset, v); + } + + public void writeOrderedDouble(long offset, double v) { + this.checkNotClosed(); + this.bytes.writeOrderedDouble(offset, v); + } + + public int length() { + this.checkNotClosed(); + return this.bytes.length(); + } + + public char charAt(int index) { + this.checkNotClosed(); + return this.bytes.charAt(index); + } + + public void readMarshallable(@NotNull Bytes in) throws IllegalStateException { + this.bytes.readMarshallable(in); + } + + public void writeMarshallable(@NotNull Bytes out) { + this.checkNotClosed(); + this.bytes.writeMarshallable(out); + } + + public void write(RandomDataInput bytes) { + this.checkNotClosed(); + this.bytes.write(bytes); + } + + public void write(Byteable byteable) { + this.checkNotClosed(); + this.bytes.write(byteable); + } + + public boolean startsWith(RandomDataInput input) { + this.checkNotClosed(); + return this.bytes.startsWith(input); + } + + @NotNull + public String toString() { + this.checkNotClosed(); + return this.bytes.toString(); + } + + @NotNull + public String toDebugString() { + this.checkNotClosed(); + return this.bytes.toDebugString(); + } + + @NotNull + public String toDebugString(long limit) { + this.checkNotClosed(); + return this.bytes.toDebugString(limit); + } + + @Override + public String toHexString(long limit) { + this.checkNotClosed(); + return this.bytes.toHexString(limit); + } + + public void toString(Appendable sb, long start, long position, long end) { + this.checkNotClosed(); + this.bytes.toString(sb, start, position, end); + } + + public void asString(Appendable appendable) { + this.checkNotClosed(); + this.bytes.asString(appendable); + } + + public CharSequence asString() { + this.checkNotClosed(); + return this.bytes.asString(); + } + + public boolean compareAndSwapDouble(long offset, double expected, double value) { + this.checkNotClosed(); + return this.bytes.compareAndSwapDouble(offset, expected, value); + } + + public File file() { + this.checkNotClosed(); + return this.bytes.file(); + } + + public boolean tryRWReadLock(long offset, long timeOutNS) throws IllegalStateException, InterruptedException { + return this.bytes.tryRWReadLock(offset, timeOutNS); + } + + public boolean tryRWWriteLock(long offset, long timeOutNS) throws IllegalStateException, InterruptedException { + return this.bytes.tryRWWriteLock(offset, timeOutNS); + } + + public void unlockRWReadLock(long offset) { + this.checkNotClosed(); + this.bytes.unlockRWReadLock(offset); + } + + public void unlockRWWriteLock(long offset) { + this.checkNotClosed(); + this.bytes.unlockRWWriteLock(offset); + } + + public Bytes slice() { + this.checkNotClosed(); + return this.bytes.slice(); + } + + public Bytes slice(long offset, long length) { + this.checkNotClosed(); + return this.bytes.slice(offset, length); + } + + public CharSequence subSequence(int start, int end) { + this.checkNotClosed(); + return this.bytes.subSequence(start, end); + } + + public Bytes bytes() { + this.checkNotClosed(); + return this.bytes.bytes(); + } + + public Bytes bytes(long offset, long length) { + this.checkNotClosed(); + return this.bytes.bytes(offset, length); + } + + public long address() { + this.checkNotClosed(); + return this.bytes.address(); + } + + public Bytes zeroOut() { + this.checkNotClosed(); + return this.bytes.zeroOut(); + } + + public Bytes zeroOut(long start, long end) { + this.checkNotClosed(); + return this.bytes.zeroOut(start, end); + } + + public Bytes zeroOut(long start, long end, boolean ifNotZero) { + this.checkNotClosed(); + return this.bytes.zeroOut(start, end, ifNotZero); + } + + public int read(@NotNull byte[] bytes, int off, int len) { + this.checkNotClosed(); + return this.bytes.read(bytes, off, len); + } + + public byte readByte() { + this.checkNotClosed(); + return this.bytes.readByte(); + } + + public byte readByte(long offset) { + this.checkNotClosed(); + return this.bytes.readByte(offset); + } + + public void readFully(@NotNull byte[] b, int off, int len) { + this.checkNotClosed(); + this.bytes.readFully(b, off, len); + } + + public void readFully(long offset, byte[] bytes, int off, int len) { + this.checkNotClosed(); + this.bytes.readFully(offset, bytes, off, len); + } + + public void readFully(@NotNull char[] data, int off, int len) { + this.checkNotClosed(); + this.bytes.readFully(data, off, len); + } + + public short readShort() { + this.checkNotClosed(); + return this.bytes.readShort(); + } + + public short readShort(long offset) { + this.checkNotClosed(); + return this.bytes.readShort(offset); + } + + public char readChar() { + this.checkNotClosed(); + return this.bytes.readChar(); + } + + public char readChar(long offset) { + this.checkNotClosed(); + return this.bytes.readChar(offset); + } + + public int readInt() { + this.checkNotClosed(); + return this.bytes.readInt(); + } + + public int readInt(long offset) { + this.checkNotClosed(); + return this.bytes.readInt(offset); + } + + public int readVolatileInt() { + this.checkNotClosed(); + return this.bytes.readVolatileInt(); + } + + public int readVolatileInt(long offset) { + this.checkNotClosed(); + return this.bytes.readVolatileInt(offset); + } + + public long readLong() { + this.checkNotClosed(); + return this.bytes.readLong(); + } + + @Override + public long readIncompleteLong(long offset) { + this.checkNotClosed(); + return this.bytes.readIncompleteLong(offset); + } + + public long readLong(long offset) { + this.checkNotClosed(); + return this.bytes.readLong(offset); + } + + public long readVolatileLong() { + this.checkNotClosed(); + return this.bytes.readVolatileLong(); + } + + public long readVolatileLong(long offset) { + this.checkNotClosed(); + return this.bytes.readVolatileLong(offset); + } + + public float readFloat() { + this.checkNotClosed(); + return this.bytes.readFloat(); + } + + public float readFloat(long offset) { + this.checkNotClosed(); + return this.bytes.readFloat(offset); + } + + public double readDouble() { + this.checkNotClosed(); + return this.bytes.readDouble(); + } + + public double readDouble(long offset) { + this.checkNotClosed(); + return this.bytes.readDouble(offset); + } + + public void write(int b) { + this.checkNotClosed(); + this.bytes.write(b); + } + + public void writeByte(long offset, int b) { + this.checkNotClosed(); + this.bytes.writeByte(offset, b); + } + + public void write(long offset, @NotNull byte[] bytes) { + this.checkNotClosed(); + this.bytes.write(offset, bytes); + } + + public void write(byte[] bytes, int off, int len) { + this.checkNotClosed(); + this.bytes.write(bytes, off, len); + } + + public void write(long offset, byte[] bytes, int off, int len) { + this.checkNotClosed(); + this.bytes.write(offset, bytes, off, len); + } + + public void writeShort(int v) { + this.checkNotClosed(); + this.bytes.writeShort(v); + } + + public void writeShort(long offset, int v) { + this.checkNotClosed(); + this.bytes.writeShort(offset, v); + } + + public void writeChar(int v) { + this.checkNotClosed(); + this.bytes.writeChar(v); + } + + public void writeChar(long offset, int v) { + this.checkNotClosed(); + this.bytes.writeChar(offset, v); + } + + public void writeInt(int v) { + this.checkNotClosed(); + this.bytes.writeInt(v); + } + + public void writeInt(long offset, int v) { + this.checkNotClosed(); + this.bytes.writeInt(offset, v); + } + + public void writeOrderedInt(int v) { + this.checkNotClosed(); + this.bytes.writeOrderedInt(v); + } + + public void writeOrderedInt(long offset, int v) { + this.checkNotClosed(); + this.bytes.writeOrderedInt(offset, v); + } + + public boolean compareAndSwapInt(long offset, int expected, int x) { + this.checkNotClosed(); + return this.bytes.compareAndSwapInt(offset, expected, x); + } + + public void writeLong(long v) { + this.checkNotClosed(); + this.bytes.writeLong(v); + } + + public void writeLong(long offset, long v) { + this.checkNotClosed(); + this.bytes.writeLong(offset, v); + } + + public void writeOrderedLong(long v) { + this.checkNotClosed(); + this.bytes.writeOrderedLong(v); + } + + public void writeOrderedLong(long offset, long v) { + this.checkNotClosed(); + this.bytes.writeOrderedLong(offset, v); + } + + public boolean compareAndSwapLong(long offset, long expected, long x) { + this.checkNotClosed(); + return this.bytes.compareAndSwapLong(offset, expected, x); + } + + public void writeFloat(float v) { + this.checkNotClosed(); + this.bytes.writeFloat(v); + } + + public void writeFloat(long offset, float v) { + this.checkNotClosed(); + this.bytes.writeFloat(offset, v); + } + + public void writeDouble(double v) { + this.checkNotClosed(); + this.bytes.writeDouble(v); + } + + public void writeDouble(long offset, double v) { + this.checkNotClosed(); + this.bytes.writeDouble(offset, v); + } + + public void readObject(Object object, int start, int end) { + this.checkNotClosed(); + this.bytes.readObject(object, start, end); + } + + public void writeObject(Object object, int start, int end) { + this.checkNotClosed(); + this.bytes.writeObject(object, start, end); + } + + public boolean compare(long offset, RandomDataInput input, long inputOffset, long len) { + this.checkNotClosed(); + return this.bytes.compare(offset, input, inputOffset, len); + } + + public long position() { + this.checkNotClosed(); + return this.bytes.position(); + } + + public Bytes position(long position) { + this.checkNotClosed(); + return this.bytes.position(position); + } + + public void write(RandomDataInput bytes, long position, long length) { + this.checkNotClosed(); + this.bytes.write(bytes, position, length); + } + + public long capacity() { + this.checkNotClosed(); + return this.bytes.capacity(); + } + + public long remaining() { + this.checkNotClosed(); + return this.bytes.remaining(); + } + + public long limit() { + this.checkNotClosed(); + return this.bytes.limit(); + } + + public Bytes limit(long limit) { + this.checkNotClosed(); + return this.bytes.limit(limit); + } + + @NotNull + public ByteOrder byteOrder() { + this.checkNotClosed(); + return this.bytes.byteOrder(); + } + + public void checkEndOfBuffer() throws IndexOutOfBoundsException { + this.bytes.checkEndOfBuffer(); + } + + public Bytes load() { + this.checkNotClosed(); + return this.bytes.load(); + } + + public void alignPositionAddr(int powerOf2) { + this.checkNotClosed(); + this.bytes.alignPositionAddr(powerOf2); + } + + public ByteBuffer sliceAsByteBuffer(ByteBuffer toReuse) { + this.checkNotClosed(); + return this.bytes.sliceAsByteBuffer(toReuse); + } + + @Override + public long nextSetBit(long fromIndex) { + this.checkNotClosed(); + return this.bytes.nextSetBit(fromIndex); + } +} diff --git a/lang/src/main/java/net/openhft/lang/io/ChronicleUnsafe.java b/lang/src/main/java/net/openhft/lang/io/ChronicleUnsafe.java new file mode 100644 index 0000000..4f1576b --- /dev/null +++ b/lang/src/main/java/net/openhft/lang/io/ChronicleUnsafe.java @@ -0,0 +1,231 @@ +/* + * Copyright (C) 2015 higherfrequencytrading.com + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as published by + * the Free Software Foundation, either version 3 of the License. + * + * 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 Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with this program. If not, see <http://www.gnu.org/licenses/>. + */ + +package net.openhft.lang.io; + +import net.openhft.lang.model.constraints.NotNull; +import sun.misc.Unsafe; + +import java.io.IOException; +import java.lang.reflect.Field; + +/** + * Created by Rob Austin + */ +public class ChronicleUnsafe { + + private final MappedFile mappedFile; + private MappedMemory mappedMemory = null; + public static final Unsafe UNSAFE; + + static { + try { + @SuppressWarnings("ALL") + Field theUnsafe = Unsafe.class.getDeclaredField("theUnsafe"); + theUnsafe.setAccessible(true); + UNSAFE = (Unsafe) theUnsafe.get(null); + } catch (Exception e) { + throw new AssertionError(e); + } + } + + private long chunkSize; + private long offset; + private final long mask; + private long last = -1; + + /** + * @param mappedFile a until that able to map block of memory to a file + * @throws IllegalStateException if the block size is not a power of 2 + */ + public ChronicleUnsafe(@NotNull MappedFile mappedFile) { + long blockSize = mappedFile.blockSize(); + + if (((blockSize & -blockSize) != blockSize)) + throw new IllegalStateException("the block size has to be a power of 2"); + + this.mappedFile = mappedFile; + this.chunkSize = mappedFile.blockSize(); + + long shift = (int) (Math.log(blockSize) / Math.log(2)); + + mask = ~((1L << shift) - 1L); + } + + public long toAddress(long address) { + return (mask & address ^ this.last) == 0 ? address + offset : toAddress0(address); + } + + public long toAddress0(long address) { + int index = (int) ((address / chunkSize)); + long remainder = address - (((long) index) * chunkSize); + + // index == 0 is the header, so we wont reference count the header + if (mappedMemory != null && mappedMemory.index() != 0) + mappedFile.release(mappedMemory); + + try { + this.mappedMemory = mappedFile.acquire(index); + } catch (IOException e) { + throw new RuntimeException(e); + } + + long result = mappedMemory.bytes().address() + remainder; + this.offset = result - address; + this.last = mask & address; + return result; + } + + public long toRemainingInChunk(long address) { + int chunk = (int) ((address / chunkSize)); + long remainder = address - (((long) chunk) * chunkSize); + + return mappedMemory.bytes().capacity() - remainder; + } + + public int arrayBaseOffset(Class<?> aClass) { + return UNSAFE.arrayBaseOffset(aClass); + } + + public int pageSize() { + throw new UnsupportedOperationException("todo (pageSize)"); + } + + public long allocateMemory(int aVoid) { + throw new UnsupportedOperationException("todo (allocateMemory)"); + } + + public long getLong(byte[] bytes, long address) { + return UNSAFE.getLong(bytes, toAddress(address)); + } + + public long getLong(Object object, long address) { + return UNSAFE.getLong(object, toAddress(address)); + } + + public void setMemory(long startAddress, long len, byte defaultValue) { + long remaining = len; + while (remaining > 0) { + long address = toAddress(startAddress); + long remainingInChunk = toRemainingInChunk(startAddress); + if (remainingInChunk > remaining) + remainingInChunk = remaining; + UNSAFE.setMemory(address, remainingInChunk, defaultValue); + startAddress += remainingInChunk; + remaining -= remainingInChunk; + } + } + + public byte getByte(long address) { + return UNSAFE.getByte(toAddress(address)); + } + + public void putByte(long address, byte value) { + UNSAFE.putByte(toAddress(address), value); + } + + public void putLong(long address, long value) { + UNSAFE.putLong(toAddress(address), value); + } + + public long getLong(long address) { + return UNSAFE.getLong(toAddress(address)); + } + + public void copyMemory(Object o, long positionAddr, Object bytes, long i, long len2) { + throw new UnsupportedOperationException("todo (copyMemory)"); + } + + public short getShort(long address) { + return UNSAFE.getShort(toAddress(address)); + } + + public char getChar(long address) { + return UNSAFE.getChar(toAddress(address)); + } + + public int getInt(long address) { + return UNSAFE.getInt(toAddress(address)); + } + + public int getIntVolatile(Object o, long address) { + return UNSAFE.getIntVolatile(o, toAddress(address)); + } + + public long getLongVolatile(Object o, long address) { + return UNSAFE.getLongVolatile(o, toAddress(address)); + } + + public float getFloat(long address) { + return UNSAFE.getFloat(toAddress(address)); + } + + public double getDouble(long address) { + return UNSAFE.getDouble(toAddress(address)); + } + + public void putShort(long address, short v) { + UNSAFE.putShort(toAddress(address), v); + } + + public void putChar(long address, char v) { + UNSAFE.putChar(toAddress(address), v); + } + + public void putInt(long address, int v) { + UNSAFE.putInt(toAddress(address), v); + } + + public void putOrderedInt(Object o, long address, int v) { + UNSAFE.putOrderedInt(o, toAddress(address), v); + } + + public boolean compareAndSwapInt(Object o, long address, int expected, int v) { + return UNSAFE.compareAndSwapInt(o, toAddress(address), expected, v); + } + + public void putOrderedLong(Object o, long address, long v) { + UNSAFE.putOrderedLong(o, toAddress(address), v); + } + + public boolean compareAndSwapLong(Object o, long address, long expected, long v) { + return UNSAFE.compareAndSwapLong(o, toAddress(address), expected, v); + } + + public void putFloat(long address, float v) { + UNSAFE.putFloat(toAddress(address), v); + } + + public void putDouble(long address, double v) { + UNSAFE.putDouble(toAddress(address), v); + } + + public void putLong(Object o, long address, long aLong) { + UNSAFE.putLong(o, toAddress(address), aLong); + } + + public void putByte(Object o, long address, byte aByte) { + UNSAFE.putByte(o, toAddress(address), aByte); + } + + public byte getByte(Object o, long address) { + return UNSAFE.getByte(o, toAddress(address)); + } + + public void copyMemory(long l, long positionAddr, long length) { + throw new UnsupportedOperationException("todo (copyMemory)"); + } +} diff --git a/lang/src/main/java/net/openhft/lang/io/DirectByteBufferBytes.java b/lang/src/main/java/net/openhft/lang/io/DirectByteBufferBytes.java new file mode 100644 index 0000000..938dd74 --- /dev/null +++ b/lang/src/main/java/net/openhft/lang/io/DirectByteBufferBytes.java @@ -0,0 +1,83 @@ +/* + * Copyright (C) 2015 higherfrequencytrading.com + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as published by + * the Free Software Foundation, either version 3 of the License. + * + * 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 Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with this program. If not, see <http://www.gnu.org/licenses/>. + */ +package net.openhft.lang.io; + +import sun.nio.ch.DirectBuffer; + +import java.nio.ByteBuffer; +import java.nio.ByteOrder; + +public class DirectByteBufferBytes extends NativeBytes implements IByteBufferBytes { + private ByteBuffer buffer; + + public DirectByteBufferBytes(int capacity) { + this(ByteBuffer.allocateDirect(capacity).order(ByteOrder.nativeOrder()), 0, capacity); + } + + public DirectByteBufferBytes(final ByteBuffer buffer) { + this(buffer, 0, buffer.capacity()); + } + + public DirectByteBufferBytes(final ByteBuffer buffer, int start, int capacity) { + super( + ((DirectBuffer) buffer).address() + start, + ((DirectBuffer) buffer).address() + capacity + ); + + this.buffer = buffer; + } + + @Override + public ByteBuffer buffer() { + return buffer; + } + + @Override + public ByteBuffer sliceAsByteBuffer(ByteBuffer toReuse) { + return sliceAsByteBuffer(toReuse, buffer); + } + + protected DirectByteBufferBytes resize(int newCapacity, boolean cleanup, boolean preserveData) { + if(newCapacity != capacity()) { + final ByteBuffer oldBuffer = this.buffer; + final long oldAddress = this.startAddr; + final long oldPosition = position(); + + if(preserveData && (oldPosition > newCapacity)) { + throw new IllegalArgumentException( + "Capacity can't be less than currently used data (size=" + oldPosition + + ", capacity=" + newCapacity + ")" + ); + } + + this.buffer = ByteBuffer.allocateDirect(newCapacity).order(ByteOrder.nativeOrder()); + setStartPositionAddress(((DirectBuffer) buffer).address()); + this.capacityAddr = this.startAddr + newCapacity; + this.limitAddr = this.capacityAddr; + + if (preserveData && (oldPosition > 0)) { + UNSAFE.copyMemory(oldAddress, this.startAddr, Math.min(newCapacity, oldPosition)); + this.positionAddr = this.startAddr + Math.min(newCapacity, oldPosition); + } + + if(cleanup) { + IOTools.clean(oldBuffer); + } + } + + return this; + } +} diff --git a/lang/src/main/java/net/openhft/lang/io/DirectBytes.java b/lang/src/main/java/net/openhft/lang/io/DirectBytes.java index 7e783e4..0f83371 100755..100644 --- a/lang/src/main/java/net/openhft/lang/io/DirectBytes.java +++ b/lang/src/main/java/net/openhft/lang/io/DirectBytes.java @@ -1,43 +1,62 @@ /* - * Copyright 2013 Peter Lawrey + * Copyright (C) 2015 higherfrequencytrading.com * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as published by + * the Free Software Foundation, either version 3 of the License. * - * http://www.apache.org/licenses/LICENSE-2.0 + * 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 Lesser General Public License for more details. * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. + * You should have received a copy of the GNU Lesser General Public License + * along with this program. If not, see <http://www.gnu.org/licenses/>. */ package net.openhft.lang.io; -import org.jetbrains.annotations.NotNull; +import net.openhft.lang.model.constraints.NotNull; + +import java.nio.ByteBuffer; +import java.util.concurrent.atomic.AtomicInteger; /** * @author peter.lawrey */ public class DirectBytes extends NativeBytes { @NotNull - private final DirectStore store; + private final BytesStore store; - DirectBytes(@NotNull DirectStore store) { - super(store.bytesMarshallerFactory, store.address, store.address, store.address + store.size); + public DirectBytes(@NotNull BytesStore store, AtomicInteger refCount) { + super(store.objectSerializer(), store.address(), store.address() + store.size(), refCount); + this.store = store; + } + + public DirectBytes(@NotNull BytesStore store, AtomicInteger refCount, long offset, long length) { + super(store.objectSerializer(), store.address() + offset, store.address() + offset + length, refCount); this.store = store; } public void positionAndSize(long offset, long size) { - if (offset < 0 || size < 0 || offset + size > store.size) + if (offset < 0 || size < 0 || offset + size > store.size()) throw new IllegalArgumentException(); - startAddr = positionAddr = store.address + offset; - limitAddr = startAddr + size; + + setStartPositionAddress(store.address() + offset); + capacityAddr = limitAddr = startAddr + size; } - public DirectStore store() { + public BytesStore store() { return store; } + + @Override + protected void cleanup() { + store.free(); + } + + @Override + public ByteBuffer sliceAsByteBuffer(ByteBuffer toReuse) { + return sliceAsByteBuffer(toReuse, store); + } } diff --git a/lang/src/main/java/net/openhft/lang/io/DirectStore.java b/lang/src/main/java/net/openhft/lang/io/DirectStore.java index 7099165..ca1b4a3 100755..100644 --- a/lang/src/main/java/net/openhft/lang/io/DirectStore.java +++ b/lang/src/main/java/net/openhft/lang/io/DirectStore.java @@ -1,45 +1,57 @@ /* - * Copyright 2013 Peter Lawrey + * Copyright (C) 2015 higherfrequencytrading.com * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as published by + * the Free Software Foundation, either version 3 of the License. * - * http://www.apache.org/licenses/LICENSE-2.0 + * 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 Lesser General Public License for more details. * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. + * You should have received a copy of the GNU Lesser General Public License + * along with this program. If not, see <http://www.gnu.org/licenses/>. */ package net.openhft.lang.io; +import net.openhft.lang.io.serialization.BytesMarshallableSerializer; import net.openhft.lang.io.serialization.BytesMarshallerFactory; +import net.openhft.lang.io.serialization.JDKZObjectSerializer; +import net.openhft.lang.io.serialization.ObjectSerializer; import net.openhft.lang.io.serialization.impl.VanillaBytesMarshallerFactory; -import org.jetbrains.annotations.NotNull; +import net.openhft.lang.model.constraints.NotNull; import sun.misc.Cleaner; +import java.io.File; +import java.util.concurrent.atomic.AtomicInteger; + /** * @author peter.lawrey */ -public class DirectStore { - protected BytesMarshallerFactory bytesMarshallerFactory; +public class DirectStore implements BytesStore, AutoCloseable { + private final ObjectSerializer objectSerializer; private final Cleaner cleaner; - protected long address; - protected long size; + private final Deallocator deallocator; + private long address; + private long size; + private final AtomicInteger refCount = new AtomicInteger(1); public DirectStore(long size) { this(new VanillaBytesMarshallerFactory(), size); } - public DirectStore(BytesMarshallerFactory bytesMarshallerFactory, long size) { + private DirectStore(BytesMarshallerFactory bytesMarshallerFactory, long size) { this(bytesMarshallerFactory, size, true); } - public DirectStore(BytesMarshallerFactory bytesMarshallerFactory, long size, boolean zeroOut) { - this.bytesMarshallerFactory = bytesMarshallerFactory; + private DirectStore(BytesMarshallerFactory bytesMarshallerFactory, long size, boolean zeroOut) { + this(BytesMarshallableSerializer.create(bytesMarshallerFactory, JDKZObjectSerializer.INSTANCE), size, zeroOut); + } + + public DirectStore(ObjectSerializer objectSerializer, long size, boolean zeroOut) { + this.objectSerializer = objectSerializer; address = NativeBytes.UNSAFE.allocateMemory(size); // System.out.println("old value " + Integer.toHexString(NativeBytes.UNSAFE.getInt(null, address))); @@ -49,36 +61,65 @@ public class DirectStore { } this.size = size; - cleaner = Cleaner.create(this, new Runnable() { - @Override - public void run() { - if (address != 0) - NativeBytes.UNSAFE.freeMemory(address); - address = DirectStore.this.size = 0; - } - }); + deallocator = new Deallocator(address); + cleaner = Cleaner.create(this, deallocator); + } + + @Override + public ObjectSerializer objectSerializer() { + return objectSerializer; } @NotNull public static DirectStore allocate(long size) { - return new DirectStore(null, size); + return new DirectStore(new VanillaBytesMarshallerFactory(), size); } @NotNull public static DirectStore allocateLazy(long size) { - return new DirectStore(null, size, false); + return new DirectStore(new VanillaBytesMarshallerFactory(), size, false); } -/* public void resize(long newSize) { - if (newSize == size) - return; - address = NativeBytes.UNSAFE.reallocateMemory(address, newSize); + /** + * Resizes this {@code DirectStore} to the {@code newSize}. + * + * <p>If {@code zeroOut} is {@code false}, the memory past the old size is not zeroed out and + * will generally be garbage. + * + * <p>{@code DirectStore} don't keep track of the child {@code DirectBytes} instances, so after + * the resize they might point to the wrong memory. Use at your own risk. + * + * @param newSize new size of this {@code DirectStore} + * @param zeroOut if the memory past the old size should be zeroed out on increasing resize + * @throws IllegalArgumentException if the {@code newSize} is not positive + */ + public void resize(long newSize, boolean zeroOut) { + if (newSize <= 0) + throw new IllegalArgumentException("Given newSize is " + newSize + + " but should be positive"); + address = deallocator.address = NativeBytes.UNSAFE.reallocateMemory(address, newSize); + if (zeroOut && newSize > size) { + NativeBytes.UNSAFE.setMemory(address + size, newSize - size, (byte) 0); + } size = newSize; - }*/ + } + @SuppressWarnings("ConstantConditions") @NotNull - public DirectBytes createSlice() { - return new DirectBytes(this); + public DirectBytes bytes() { + boolean debug = false; + assert debug = true; + return debug ? new BoundsCheckingDirectBytes(this, refCount) : new DirectBytes(this, refCount); + } + + @NotNull + public DirectBytes bytes(long offset, long length) { + return new DirectBytes(this, refCount, offset, length); + } + + @Override + public long address() { + return address; } public void free() { @@ -89,8 +130,41 @@ public class DirectStore { return size; } - public BytesMarshallerFactory bytesMarshallerFactory() { - return bytesMarshallerFactory; + public static BytesStore allocateLazy(long sizeInBytes, ObjectSerializer objectSerializer) { + return new DirectStore(objectSerializer, sizeInBytes, false); + } + + @Override + public File file() { + return null; + } + + /** + * calls free + */ + @Override + public void close() { + free(); } + /** + * Static nested class instead of anonymous because the latter would hold a strong reference to + * this DirectStore preventing it from becoming phantom-reachable. + */ + private static class Deallocator implements Runnable { + private volatile long address; + + Deallocator(long address) { + assert address != 0; + this.address = address; + } + + @Override + public void run() { + if (address == 0) + return; + NativeBytes.UNSAFE.freeMemory(address); + address = 0; + } + } } diff --git a/lang/src/main/java/net/openhft/lang/io/EscapingStopCharTester.java b/lang/src/main/java/net/openhft/lang/io/EscapingStopCharTester.java new file mode 100755 index 0000000..235f55e --- /dev/null +++ b/lang/src/main/java/net/openhft/lang/io/EscapingStopCharTester.java @@ -0,0 +1,46 @@ +/* + * Copyright (C) 2015 higherfrequencytrading.com + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as published by + * the Free Software Foundation, either version 3 of the License. + * + * 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 Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with this program. If not, see <http://www.gnu.org/licenses/>. + */ + +package net.openhft.lang.io; + +/** + * Created by peter.lawrey on 16/01/15. + */ +public class EscapingStopCharTester implements StopCharTester { + private final StopCharTester sct; + private boolean escaped = false; + + EscapingStopCharTester(StopCharTester sct) { + this.sct = sct; + } + + public static StopCharTester escaping(StopCharTester sct) { + return new EscapingStopCharTester(sct); + } + + @Override + public boolean isStopChar(int ch) throws IllegalStateException { + if (escaped) { + escaped = false; + return false; + } + if (ch == '\\') { + escaped = true; + return false; + } + return sct.isStopChar(ch); + } +} diff --git a/lang/src/main/java/net/openhft/lang/io/FileLifecycleListener.java b/lang/src/main/java/net/openhft/lang/io/FileLifecycleListener.java new file mode 100755 index 0000000..71ddec4 --- /dev/null +++ b/lang/src/main/java/net/openhft/lang/io/FileLifecycleListener.java @@ -0,0 +1,59 @@ +/* + * Copyright (C) 2015 higherfrequencytrading.com + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as published by + * the Free Software Foundation, either version 3 of the License. + * + * 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 Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with this program. If not, see <http://www.gnu.org/licenses/>. + */ +package net.openhft.lang.io; + +import org.slf4j.LoggerFactory; + +import java.io.File; + +/** + * Created by peter.lawrey on 05/08/2015. + */ +public interface FileLifecycleListener { + enum EventType { + NEW, + MMAP, + UNMAP, + GROW, + SYNC, + DELETE, + CLOSE + } + + enum FileLifecycleListeners implements FileLifecycleListener { + IGNORE { + @Override + public void onEvent(EventType type, File file, long timeInNanos) { + } + }, + CONSOLE { + @Override + public void onEvent(EventType type, File file, long timeInNanos) { + System.out.println( + "File " + file + " took " + timeInNanos / 1000 / 1e3 + " ms. to " + type); + } + }, + LOG { + @Override + public void onEvent(EventType type, File file, long timeInNanos) { + LoggerFactory.getLogger(FileLifecycleListeners.class).info( + "File " + file + " took " + timeInNanos / 1000 / 1e3 + " ms. to " + type); + } + } + } + + void onEvent(EventType type, File file, long timeInNanos); +} diff --git a/lang/src/main/java/net/openhft/lang/io/IByteBufferBytes.java b/lang/src/main/java/net/openhft/lang/io/IByteBufferBytes.java new file mode 100755 index 0000000..43997b2 --- /dev/null +++ b/lang/src/main/java/net/openhft/lang/io/IByteBufferBytes.java @@ -0,0 +1,29 @@ +/* + * Copyright (C) 2015 higherfrequencytrading.com + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as published by + * the Free Software Foundation, either version 3 of the License. + * + * 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 Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with this program. If not, see <http://www.gnu.org/licenses/>. + */ + +package net.openhft.lang.io; + +import java.nio.ByteBuffer; + +/** + * Created by peter.lawrey on 27/01/15. + */ +public interface IByteBufferBytes extends Bytes { + /** + * Obtain the underlying ByteBuffer + */ + ByteBuffer buffer(); +} diff --git a/lang/src/main/java/net/openhft/lang/io/IOTools.java b/lang/src/main/java/net/openhft/lang/io/IOTools.java index 854e8fe..b87cb68 100755..100644 --- a/lang/src/main/java/net/openhft/lang/io/IOTools.java +++ b/lang/src/main/java/net/openhft/lang/io/IOTools.java @@ -1,29 +1,32 @@ /* - * Copyright 2013 Peter Lawrey + * Copyright (C) 2015 higherfrequencytrading.com * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as published by + * the Free Software Foundation, either version 3 of the License. * - * http://www.apache.org/licenses/LICENSE-2.0 + * 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 Lesser General Public License for more details. * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. + * You should have received a copy of the GNU Lesser General Public License + * along with this program. If not, see <http://www.gnu.org/licenses/>. */ package net.openhft.lang.io; -import org.jetbrains.annotations.NotNull; -import org.jetbrains.annotations.Nullable; +import net.openhft.lang.model.constraints.NotNull; +import net.openhft.lang.model.constraints.Nullable; +import org.slf4j.LoggerFactory; +import sun.misc.Cleaner; +import sun.nio.ch.DirectBuffer; import java.io.Closeable; import java.io.File; import java.io.IOException; +import java.nio.ByteBuffer; import java.nio.charset.Charset; -import java.util.logging.Logger; /** * @author peter.lawrey @@ -61,7 +64,7 @@ public enum IOTools { deleteDir(new File(dirPath)); } - public static void deleteDir(File dir) { + private static void deleteDir(File dir) { // delete one level. if (dir.isDirectory()) { File[] files = dir.listFiles(); @@ -69,11 +72,19 @@ public enum IOTools { for (File file : files) if (file.isDirectory()) { deleteDir(file); + } else if (!file.delete()) { - Logger.getLogger(IOTools.class.getName()).info("... unable to delete " + file); + LoggerFactory.getLogger(IOTools.class).info("... unable to delete {}", file); } - } dir.delete(); } + + public static void clean(ByteBuffer bb) { + if (bb instanceof DirectBuffer) { + Cleaner cl = ((DirectBuffer) bb).cleaner(); + if (cl != null) + cl.clean(); + } + } } diff --git a/lang/src/main/java/net/openhft/lang/io/MappedFile.java b/lang/src/main/java/net/openhft/lang/io/MappedFile.java index f4907b9..6174cbd 100644..100755 --- a/lang/src/main/java/net/openhft/lang/io/MappedFile.java +++ b/lang/src/main/java/net/openhft/lang/io/MappedFile.java @@ -1,22 +1,23 @@ /* - * Copyright 2013 Peter Lawrey + * Copyright (C) 2015 higherfrequencytrading.com * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as published by + * the Free Software Foundation, either version 3 of the License. * - * http://www.apache.org/licenses/LICENSE-2.0 + * 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 Lesser General Public License for more details. * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. + * You should have received a copy of the GNU Lesser General Public License + * along with this program. If not, see <http://www.gnu.org/licenses/>. */ package net.openhft.lang.io; -import org.jetbrains.annotations.NotNull; +import net.openhft.lang.model.constraints.NotNull; +import org.slf4j.LoggerFactory; import java.io.FileNotFoundException; import java.io.IOException; @@ -26,29 +27,29 @@ import java.nio.MappedByteBuffer; import java.nio.channels.FileChannel; import java.util.ArrayList; import java.util.List; -import java.util.logging.Logger; /** * This manages the full life cycle of a file and its mappings. */ public class MappedFile { - private final FileChannel fileChannel; - private final String basePath; + private final String filename; private final long blockSize; private final long overlapSize; + + private final FileChannel fileChannel; private final List<MappedMemory> maps = new ArrayList<MappedMemory>(); // short list of the last two mappings. private volatile MappedMemory map0, map1; - public MappedFile(String basePath, long blockSize) throws FileNotFoundException { - this(basePath, blockSize, 0L); + public MappedFile(String filename, long blockSize) throws FileNotFoundException { + this(filename, blockSize, 0L); } - public MappedFile(String basePath, long blockSize, long overlapSize) throws FileNotFoundException { - this.basePath = basePath; + public MappedFile(String filename, long blockSize, long overlapSize) throws FileNotFoundException { + this.filename = filename; this.blockSize = blockSize; this.overlapSize = overlapSize; - fileChannel = new RandomAccessFile(basePath, "rw").getChannel(); + fileChannel = new RandomAccessFile(filename, "rw").getChannel(); } public static MappedByteBuffer getMap(@NotNull FileChannel fileChannel, long start, int size) throws IOException { @@ -66,26 +67,22 @@ public class MappedFile { if (e.getMessage() == null || !e.getMessage().endsWith("user-mapped section open")) { throw e; } - if (i < 10) - //noinspection CallToThreadYield - Thread.yield(); - else - try { - //noinspection BusyWait - Thread.sleep(1); - } catch (InterruptedException ignored) { - Thread.currentThread().interrupt(); - throw e; - } + try { + //noinspection BusyWait + Thread.sleep(1); + } catch (InterruptedException ignored) { + Thread.currentThread().interrupt(); + throw e; + } } } } - public MappedMemory acquire(long index) throws IOException { - return acquire(index, false); + public String name() { + return filename; } - public MappedMemory acquire(long index, boolean prefetch) throws IOException { + public MappedMemory acquire(long index) throws IOException { MappedMemory map0 = this.map0, map1 = this.map1; if (map0 != null && map0.index() == index) { map0.reserve(); @@ -95,10 +92,29 @@ public class MappedFile { map1.reserve(); return map1; } - return acquire0(index, prefetch); + return acquire0(index); + } + + /** + * gets the refCount a given index, or returns 0, if the there is no mapping for this index + * + * @param index + * @return the mapping at this {@code index} + * @throws IndexOutOfBoundsException if the index is out of range + */ + public int getRefCount(long index) { + try { + for (MappedMemory m : maps) { + if (m.index() == index) + return m.refCount(); + } + } catch (Exception e) { + return 0; + } + return 0; } - private synchronized MappedMemory acquire0(long index, boolean prefetch) throws IOException { + private synchronized MappedMemory acquire0(long index) throws IOException { if (map1 != null) map1.release(); map1 = map0; @@ -130,8 +146,10 @@ public class MappedFile { count++; } } - if (count > 1) - Logger.getLogger(MappedFile.class.getName()).info(basePath + " memory mappings left unreleased, num= " + count); + if (count > 1) { + LoggerFactory.getLogger(MappedFile.class).info("{} memory mappings left unreleased, num= {}", filename, count); + } + maps.clear(); fileChannel.close(); } @@ -143,4 +161,17 @@ public class MappedFile { return 0; } } + + public long blockSize() { + return blockSize; + } + + public void release(MappedMemory mappedMemory) { + if (mappedMemory.release()) { + if (map0 == mappedMemory) + map0 = null; + if (map1 == mappedMemory) + map1 = null; + } + } } diff --git a/lang/src/main/java/net/openhft/lang/io/MappedMemory.java b/lang/src/main/java/net/openhft/lang/io/MappedMemory.java index df97ef5..6fe488d 100644 --- a/lang/src/main/java/net/openhft/lang/io/MappedMemory.java +++ b/lang/src/main/java/net/openhft/lang/io/MappedMemory.java @@ -1,57 +1,66 @@ /* - * Copyright 2013 Peter Lawrey + * Copyright (C) 2015 higherfrequencytrading.com * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as published by + * the Free Software Foundation, either version 3 of the License. * - * http://www.apache.org/licenses/LICENSE-2.0 + * 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 Lesser General Public License for more details. * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. + * You should have received a copy of the GNU Lesser General Public License + * along with this program. If not, see <http://www.gnu.org/licenses/>. */ package net.openhft.lang.io; -import sun.misc.Cleaner; +import net.openhft.lang.ReferenceCounted; +import net.openhft.lang.io.serialization.ObjectSerializer; import sun.nio.ch.DirectBuffer; +import java.io.File; import java.nio.MappedByteBuffer; import java.util.concurrent.atomic.AtomicInteger; -public class MappedMemory { +public class MappedMemory implements ReferenceCounted, BytesStore { private final MappedByteBuffer buffer; + private final DirectByteBufferBytes bytes; private final long index; - private final AtomicInteger refCount = new AtomicInteger(1); + private final AtomicInteger refCount = new AtomicInteger(0); private volatile boolean unmapped = false; public MappedMemory(MappedByteBuffer buffer, long index) { this.buffer = buffer; this.index = index; - } - - private static void unmap(MappedByteBuffer bb) { - Cleaner cl = ((DirectBuffer) bb).cleaner(); - if (cl != null) - cl.clean(); + bytes = new DirectByteBufferBytes(buffer); } public long index() { return index; } + @Override public void reserve() { if (unmapped) throw new IllegalStateException(); refCount.incrementAndGet(); } - public void release() { + /** + * @return true if release cause the ref count to drop to zero and the resource was freed + */ + @Override + public boolean release() { if (unmapped) throw new IllegalStateException(); - if (refCount.decrementAndGet() > 0) return; + if (refCount.decrementAndGet() > 0) return false; close(); + return true; + } + + @Override + public int refCount() { + return refCount.get(); } public MappedByteBuffer buffer() { @@ -62,8 +71,33 @@ public class MappedMemory { return ((DirectBuffer) buffer).address(); } - public int refCount() { - return refCount.get(); + @Override + public long size() { + return bytes.capacity(); + } + + @Override + public void free() { + throw new UnsupportedOperationException(); + } + + @Override + public ObjectSerializer objectSerializer() { + return null; + } + + @Override + public File file() { + throw new UnsupportedOperationException(); + } + + public Bytes bytes() { + return bytes; + } + + @Override + public Bytes bytes(long offset, long length) { + throw new UnsupportedOperationException(); } public static void release(MappedMemory mapmem) { @@ -77,7 +111,7 @@ public class MappedMemory { } public void close() { - unmap(buffer); + IOTools.clean(buffer); unmapped = true; } } diff --git a/lang/src/main/java/net/openhft/lang/io/MappedNativeBytes.java b/lang/src/main/java/net/openhft/lang/io/MappedNativeBytes.java new file mode 100755 index 0000000..79f4e9c --- /dev/null +++ b/lang/src/main/java/net/openhft/lang/io/MappedNativeBytes.java @@ -0,0 +1,730 @@ +/* + * Copyright (C) 2015 higherfrequencytrading.com + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as published by + * the Free Software Foundation, either version 3 of the License. + * + * 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 Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with this program. If not, see <http://www.gnu.org/licenses/>. + */ + +package net.openhft.lang.io; + +import net.openhft.lang.io.serialization.ObjectSerializer; +import org.jetbrains.annotations.NotNull; +import sun.misc.Unsafe; + +import java.io.EOFException; +import java.lang.reflect.Field; +import java.nio.ByteBuffer; +import java.nio.ByteOrder; +import java.util.concurrent.atomic.AtomicInteger; + +/** + * works in conjunction with a MappedFile to map additional memory blocks when required This + * implementation is very similar to {@code NativeBytes}, accept that the memory address resolution + * is left to ChronicleUnsafe, rather than being part of this class. + */ +public class MappedNativeBytes extends AbstractBytes { + + @NotNull + public final ThreadLocal<ChronicleUnsafe> threadLocal = new ThreadLocal<ChronicleUnsafe>(); + + static final int BYTES_OFFSET; + static final int CHARS_OFFSET; + + static { + try { + @SuppressWarnings("ALL") + Field theUnsafe = Unsafe.class.getDeclaredField("theUnsafe"); + theUnsafe.setAccessible(true); + Unsafe unsafe = (Unsafe) theUnsafe.get(null); + BYTES_OFFSET = unsafe.arrayBaseOffset(byte[].class); + CHARS_OFFSET = unsafe.arrayBaseOffset(char[].class); + } catch (Exception e) { + throw new AssertionError(e); + } + } + private final boolean isSingleThreaded; + + protected long start; + protected long position; + protected long limit; + protected long capacity; + private final MappedFile mappedFile; + private final ChronicleUnsafe chronicleUnsafe; + + public MappedNativeBytes(@NotNull final MappedFile mappedFile, boolean isSingleThreaded) { + this.isSingleThreaded = isSingleThreaded; + this.mappedFile = mappedFile; + this.start = 0; + this.position = start; + this.limit = this.capacity = Long.MAX_VALUE; + this.chronicleUnsafe = (isSingleThreaded) ? new ChronicleUnsafe(mappedFile) : null; + } + + public MappedNativeBytes(ObjectSerializer objectSerializer, + long sliceStart, + long capacity, + @NotNull AtomicInteger refCount, + @NotNull MappedFile mappedFile, + boolean singleThreaded) { + this.isSingleThreaded = singleThreaded; + setObjectSerializer(objectSerializer); + this.start = sliceStart; + this.position = 0; + this.capacity = capacity; + this.refCount.set(refCount.get()); + this.mappedFile = mappedFile; + this.chronicleUnsafe = (isSingleThreaded) ? new ChronicleUnsafe(mappedFile) : null; + } + + @Override + public MappedNativeBytes slice() { + return new MappedNativeBytes(objectSerializer(), position, limit, refCount, mappedFile, isSingleThreaded); + } + + @Override + public MappedNativeBytes slice(long offset, long length) { + long sliceStart = position + offset; + assert sliceStart >= start && sliceStart < capacity; + long sliceEnd = sliceStart + length; + assert sliceEnd > sliceStart && sliceEnd <= capacity; + return new MappedNativeBytes(objectSerializer(), sliceStart, sliceEnd, refCount, mappedFile, isSingleThreaded); + } + + @Override + public CharSequence subSequence(int start, int end) { + long subStart = position + start; + if (subStart < position || subStart > limit) + throw new IndexOutOfBoundsException(); + long subEnd = position + end; + if (subEnd < subStart || subEnd > limit) + throw new IndexOutOfBoundsException(); + if (start == end) + return ""; + return new MappedNativeBytes(objectSerializer(), subStart, subEnd, refCount, mappedFile, isSingleThreaded); + } + + @Override + public MappedNativeBytes bytes() { + return new MappedNativeBytes(objectSerializer(), start, capacity, refCount, mappedFile, isSingleThreaded); + } + + @Override + public MappedNativeBytes bytes(long offset, long length) { + long sliceStart = start + offset; + assert sliceStart >= start && sliceStart < capacity; + long sliceEnd = sliceStart + length; + assert sliceEnd > sliceStart && sliceEnd <= capacity; + return new MappedNativeBytes(objectSerializer(), sliceStart, sliceEnd, refCount, mappedFile, isSingleThreaded); + } + + @Override + public long address() { + return start; + } + + @Override + public Bytes zeroOut() { + clear(); + getChronicleUnsafe().setMemory(start, capacity(), (byte) 0); + return this; + } + + @Override + public Bytes zeroOut(long start, long end) { + if (start < 0 || end > limit()) + throw new IllegalArgumentException("start: " + start + ", end: " + end); + if (start >= end) + return this; + getChronicleUnsafe().setMemory(this.start + start, end - start, (byte) 0); + return this; + } + + @Override + public Bytes zeroOut(long start, long end, boolean ifNotZero) { + return ifNotZero ? zeroOutDirty(start, end) : zeroOut(start, end); + } + + private Bytes zeroOutDirty(long start, long end) { + if (start < 0 || end > limit()) + throw new IllegalArgumentException("start: " + start + ", end: " + end); + if (start >= end) + return this; + // get unaligned leading bytes + ChronicleUnsafe unsafe = getChronicleUnsafe(); + while (start < end && (start & 7) != 0) { + byte b = unsafe.getByte(this.start + start); + if (b != 0) + unsafe.putByte(this.start + start, (byte) 0); + start++; + } + // check 64-bit aligned access + while (start < end - 7) { + long l = unsafe.getLong(this.start + start); + if (l != 0) + unsafe.putLong(this.start + start, 0L); + start += 8; + } + // check unaligned tail + while (start < end) { + byte b = unsafe.getByte(this.start + start); + if (b != 0) + unsafe.putByte(this.start + start, (byte) 0); + start++; + } + return this; + } + + @Override + public int read(@NotNull byte[] bytes, int off, int len) { + if (len < 0 || off < 0 || off + len > bytes.length) + throw new IllegalArgumentException(); + long left = remaining(); + if (left <= 0) return -1; + int len2 = (int) Math.min(len, left); + getChronicleUnsafe().copyMemory(null, position, bytes, BYTES_OFFSET + off, len2); + addPosition(len2); + return len2; + } + + @Override + public byte readByte() { + byte aByte = getChronicleUnsafe().getByte(position); + addPosition(1); + return aByte; + } + + @Override + public byte readByte(long offset) { + return getChronicleUnsafe().getByte(start + offset); + } + + @Override + public void readFully(@NotNull byte[] b, int off, int len) { + checkArrayOffs(b.length, off, len); + long left = remaining(); + if (left < len) + throw new IllegalStateException(new EOFException()); + getChronicleUnsafe().copyMemory(null, position, b, BYTES_OFFSET + off, len); + addPosition(len); + } + + @Override + public void readFully(long offset, byte[] bytes, int off, int len) { + checkArrayOffs(bytes.length, off, len); + getChronicleUnsafe().copyMemory(null, start + offset, bytes, BYTES_OFFSET + off, len); + } + + @Override + public void readFully(@NotNull char[] data, int off, int len) { + checkArrayOffs(data.length, off, len); + long bytesOff = off * 2L; + long bytesLen = len * 2L; + long left = remaining(); + if (left < bytesLen) + throw new IllegalStateException(new EOFException()); + getChronicleUnsafe().copyMemory(null, position, data, BYTES_OFFSET + bytesOff, bytesLen); + addPosition(bytesLen); + } + + @Override + public short readShort() { + short s = getChronicleUnsafe().getShort(position); + addPosition(2); + return s; + } + + @Override + public short readShort(long offset) { + return getChronicleUnsafe().getShort(start + offset); + } + + @Override + public char readChar() { + char ch = getChronicleUnsafe().getChar(position); + addPosition(2); + return ch; + } + + @Override + public char readChar(long offset) { + return getChronicleUnsafe().getChar(start + offset); + } + + @Override + public int readInt() { + int i = getChronicleUnsafe().getInt(position); + addPosition(4); + return i; + } + + @Override + public int readInt(long offset) { + return getChronicleUnsafe().getInt(start + offset); + } + + @Override + public int readVolatileInt() { + int i = getChronicleUnsafe().getIntVolatile(null, position); + addPosition(4); + return i; + } + + @Override + public int readVolatileInt(long offset) { + return getChronicleUnsafe().getIntVolatile(null, start + offset); + } + + @Override + public long readLong() { + long l = getChronicleUnsafe().getLong(position); + addPosition(8); + return l; + } + + @Override + public long readLong(long offset) { + return getChronicleUnsafe().getLong(start + offset); + } + + @Override + public long readVolatileLong() { + long l = getChronicleUnsafe().getLongVolatile(null, position); + addPosition(8); + return l; + } + + @Override + public long readVolatileLong(long offset) { + return getChronicleUnsafe().getLongVolatile(null, start + offset); + } + + @Override + public float readFloat() { + float f = getChronicleUnsafe().getFloat(position); + addPosition(4); + return f; + } + + @Override + public float readFloat(long offset) { + return getChronicleUnsafe().getFloat(start + offset); + } + + @Override + public double readDouble() { + double d = getChronicleUnsafe().getDouble(position); + addPosition(8); + return d; + } + + @Override + public double readDouble(long offset) { + return getChronicleUnsafe().getDouble(start + offset); + } + + @Override + public void write(int b) { + getChronicleUnsafe().putByte(position, (byte) b); + incrementPositionAddr(1); + } + + @Override + public void writeByte(long offset, int b) { + offsetChecks(offset, 1L); + getChronicleUnsafe().putByte(start + offset, (byte) b); + } + + @Override + public void write(long offset, @NotNull byte[] bytes) { + if (offset < 0 || offset + bytes.length > capacity()) + throw new IllegalArgumentException(); + getChronicleUnsafe().copyMemory(bytes, BYTES_OFFSET, null, start + offset, bytes.length); + addPosition(bytes.length); + } + + @Override + public void write(byte[] bytes, int off, int len) { + if (off < 0 || off + len > bytes.length || len > remaining()) + throw new IllegalArgumentException(); + getChronicleUnsafe().copyMemory(bytes, BYTES_OFFSET + off, null, position, len); + addPosition(len); + } + + @Override + public void write(long offset, byte[] bytes, int off, int len) { + if (offset < 0 || off + len > bytes.length || offset + len > capacity()) + throw new IllegalArgumentException(); + getChronicleUnsafe().copyMemory(bytes, BYTES_OFFSET + off, null, start + offset, len); + } + + @Override + public void writeShort(int v) { + positionChecks(position + 2L); + getChronicleUnsafe().putShort(position, (short) v); + position += 2L; + } + + private long incrementPositionAddr(long value) { + positionAddr(positionAddr() + value); + return positionAddr(); + } + + @Override + public void writeShort(long offset, int v) { + offsetChecks(offset, 2L); + getChronicleUnsafe().putShort(start + offset, (short) v); + } + + @Override + public void writeChar(int v) { + positionChecks(position + 2L); + getChronicleUnsafe().putChar(position, (char) v); + position += 2L; + } + + void addPosition(long delta) { + positionAddr(positionAddr() + delta); + } + + @Override + public void writeChar(long offset, int v) { + offsetChecks(offset, 2L); + getChronicleUnsafe().putChar(start + offset, (char) v); + } + + @Override + public void writeInt(int v) { + positionChecks(position + 4L); + getChronicleUnsafe().putInt(position, v); + position += 4L; + } + + @Override + public void writeInt(long offset, int v) { + offsetChecks(offset, 4L); + getChronicleUnsafe().putInt(start + offset, v); + } + + @Override + public void writeOrderedInt(int v) { + positionChecks(position + 4L); + getChronicleUnsafe().putOrderedInt(null, position, v); + position += 4L; + } + + @Override + public void writeOrderedInt(long offset, int v) { + offsetChecks(offset, 4L); + getChronicleUnsafe().putOrderedInt(null, start + offset, v); + } + + @Override + public boolean compareAndSwapInt(long offset, int expected, int x) { + offsetChecks(offset, 4L); + return getChronicleUnsafe().compareAndSwapInt(null, start + offset, expected, x); + } + + @Override + public void writeLong(long v) { + positionChecks(position + 8L); + getChronicleUnsafe().putLong(position, v); + position += 8L; + } + + @Override + public void writeLong(long offset, long v) { + offsetChecks(offset, 8L); + getChronicleUnsafe().putLong(start + offset, v); + } + + @Override + public void writeOrderedLong(long v) { + positionChecks(position + 8L); + getChronicleUnsafe().putOrderedLong(null, position, v); + position += 8L; + } + + @Override + public void writeOrderedLong(long offset, long v) { + offsetChecks(offset, 8L); + getChronicleUnsafe().putOrderedLong(null, start + offset, v); + } + + @Override + public boolean compareAndSwapLong(long offset, long expected, long x) { + offsetChecks(offset, 8L); + return getChronicleUnsafe().compareAndSwapLong(null, start + offset, expected, x); + } + + @Override + public void writeFloat(float v) { + positionChecks(position + 4L); + getChronicleUnsafe().putFloat(position, v); + position += 4L; + } + + @Override + public void writeFloat(long offset, float v) { + offsetChecks(offset, 4L); + getChronicleUnsafe().putFloat(start + offset, v); + } + + @Override + public void writeDouble(double v) { + positionChecks(position + 8L); + getChronicleUnsafe().putDouble(position, v); + position += 8L; + } + + @Override + public void writeDouble(long offset, double v) { + offsetChecks(offset, 8L); + getChronicleUnsafe().putDouble(start + offset, v); + } + + @Override + public void readObject(Object object, int start, int end) { + int len = end - start; + if (position + len >= limit) + throw new IndexOutOfBoundsException("Length out of bounds len: " + len); + + ChronicleUnsafe unsafe = getChronicleUnsafe(); + for (; len >= 8; len -= 8) { + unsafe.putLong(object, (long) start, unsafe.getLong(position)); + incrementPositionAddr(8L); + start += 8; + } + for (; len > 0; len--) { + unsafe.putByte(object, (long) start, unsafe.getByte(position)); + incrementPositionAddr(1L); + start++; + } + } + + @Override + public void writeObject(Object object, int start, int end) { + int len = end - start; + + ChronicleUnsafe unsafe = getChronicleUnsafe(); + for (; len >= 8; len -= 8) { + positionChecks(position + 8L); + unsafe.putLong(position, unsafe.getLong(object, (long) start)); + position += 8; + start += 8; + } + for (; len > 0; len--) { + positionChecks(position + 1L); + unsafe.putByte(position, unsafe.getByte(object, (long) start)); + position++; + start++; + } + } + + @Override + public boolean compare(long offset, RandomDataInput input, long inputOffset, long len) { + if (offset < 0 || inputOffset < 0 || len < 0) + throw new IndexOutOfBoundsException(); + if (offset + len < 0 || offset + len > capacity() || inputOffset + len < 0 || + inputOffset + len > input.capacity()) { + return false; + } + long i = 0L; + ChronicleUnsafe unsafe = getChronicleUnsafe(); + for (; i < len - 7L; i += 8L) { + if (unsafe.getLong(start + offset + i) != input.readLong(inputOffset + i)) + return false; + } + if (i < len - 3L) { + if (unsafe.getInt(start + offset + i) != input.readInt(inputOffset + i)) + return false; + i += 4L; + } + if (i < len - 1L) { + if (unsafe.getChar(start + offset + i) != input.readChar(inputOffset + i)) + return false; + i += 2L; + } + if (i < len) { + if (unsafe.getByte(start + offset + i) != input.readByte(inputOffset + i)) + return false; + } + return true; + } + + @Override + public long position() { + return (position - start); + } + + @Override + public MappedNativeBytes position(long position) { + if (position < 0 || position > limit()) + throw new IndexOutOfBoundsException("position: " + position + " limit: " + limit()); + + positionAddr(start + position); + return this; + } + + /** + * Change the position acknowleging there is no thread safety assumptions. Best effort setting + * is fine. * + * + * @param position to set if we can. + * @return this + */ + public MappedNativeBytes lazyPosition(long position) { + if (position < 0 || position > limit()) + throw new IndexOutOfBoundsException("position: " + position + " limit: " + limit()); + + // assume we don't need to no check thread safety. + + positionAddr(start + position); + return this; + } + + @Override + public void write(RandomDataInput bytes, long position, long length) { + if (length > remaining()) + throw new IllegalArgumentException("Attempt to write " + length + " bytes with " + remaining() + " remaining"); + if (bytes instanceof MappedNativeBytes) { + getChronicleUnsafe().copyMemory(((MappedNativeBytes) bytes).start + position, this.position, length); + skip(length); + + } else { + super.write(bytes, position, length); + } + } + + @Override + public long capacity() { + return (capacity - start); + } + + @Override + public long remaining() { + return (limit - position); + } + + @Override + public long limit() { + return (limit - start); + } + + @Override + public MappedNativeBytes limit(long limit) { + if (limit < 0 || limit > capacity()) { + throw new IllegalArgumentException("limit: " + limit + " capacity: " + capacity()); + } + + this.limit = start + limit; + return this; + } + + @NotNull + @Override + public ByteOrder byteOrder() { + return ByteOrder.nativeOrder(); + } + + @Override + public void checkEndOfBuffer() throws IndexOutOfBoundsException { + if (position() > limit()) { + throw new IndexOutOfBoundsException( + "position is beyond the end of the buffer " + position() + " > " + limit()); + } + } + + public long startAddr() { + return start; + } + + long capacityAddr() { + return capacity; + } + + @Override + protected void cleanup() { + // TODO nothing to do. + } + + @Override + public Bytes load() { + ChronicleUnsafe unsafe = getChronicleUnsafe(); + int pageSize = unsafe.pageSize(); + for (long addr = start; addr < capacity; addr += pageSize) + unsafe.getByte(addr); + return this; + } + + public void alignPositionAddr(int powerOf2) { + long value = (position + powerOf2 - 1) & ~(powerOf2 - 1); + positionAddr(value); + } + + public void positionAddr(long positionAddr) { + positionChecks(positionAddr); + this.position = positionAddr; + } + + void positionChecks(long positionAddr) { + assert actualPositionChecks(positionAddr); + } + + boolean actualPositionChecks(long positionAddr) { + if (positionAddr < start) + throw new IndexOutOfBoundsException("position before the start by " + (start - positionAddr) + " bytes"); + if (positionAddr > limit) + throw new IndexOutOfBoundsException("position after the limit by " + (positionAddr - limit) + " bytes"); + + return true; + } + + void offsetChecks(long offset, long len) { + assert actualOffsetChecks(offset, len); + } + + boolean actualOffsetChecks(long offset, long len) { + if (offset < 0L || offset + len > capacity()) + throw new IndexOutOfBoundsException("offset out of bounds: " + offset + ", len: " + + len + ", capacity: " + capacity()); + return true; + } + + public long positionAddr() { + return position; + } + + @Override + public ByteBuffer sliceAsByteBuffer(ByteBuffer toReuse) { + return sliceAsByteBuffer(toReuse, null); + } + + protected ByteBuffer sliceAsByteBuffer(ByteBuffer toReuse, Object att) { + return ByteBufferReuse.INSTANCE.reuse(position, (int) remaining(), att, toReuse); + } + + // todo : we should move this lookup further up the stack, so that it can be done, just ONCE, for example once by a single threaded appender + // todo : hence the constructor should be give then instance of chronicleUnsafe to use + @NotNull + public ChronicleUnsafe getChronicleUnsafe() { + if (isSingleThreaded) + return chronicleUnsafe; + + ChronicleUnsafe chronicleUnsafe = threadLocal.get(); + if (chronicleUnsafe == null) { + chronicleUnsafe = new ChronicleUnsafe(mappedFile); + threadLocal.set(chronicleUnsafe); + } + + return chronicleUnsafe; + } +} diff --git a/lang/src/main/java/net/openhft/lang/io/MappedStore.java b/lang/src/main/java/net/openhft/lang/io/MappedStore.java new file mode 100644 index 0000000..df6b9f2 --- /dev/null +++ b/lang/src/main/java/net/openhft/lang/io/MappedStore.java @@ -0,0 +1,52 @@ +/* + * Copyright (C) 2015 higherfrequencytrading.com + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as published by + * the Free Software Foundation, either version 3 of the License. + * + * 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 Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with this program. If not, see <http://www.gnu.org/licenses/>. + */ + +package net.openhft.lang.io; + +import net.openhft.lang.io.serialization.BytesMarshallableSerializer; +import net.openhft.lang.io.serialization.BytesMarshallerFactory; +import net.openhft.lang.io.serialization.JDKZObjectSerializer; +import net.openhft.lang.io.serialization.ObjectSerializer; +import net.openhft.lang.io.serialization.impl.VanillaBytesMarshallerFactory; + +import java.io.File; +import java.io.IOException; +import java.nio.channels.FileChannel; + +public class MappedStore extends AbstractMappedStore { + public MappedStore(File file, FileChannel.MapMode mode, long size) throws IOException { + this(file, mode, size, new VanillaBytesMarshallerFactory()); + } + + @Deprecated + public MappedStore(File file, FileChannel.MapMode mode, long size, + BytesMarshallerFactory bytesMarshallerFactory) throws IOException { + this(file, mode, size, BytesMarshallableSerializer.create( + bytesMarshallerFactory, JDKZObjectSerializer.INSTANCE)); + } + + public MappedStore(File file, FileChannel.MapMode mode, long size, + ObjectSerializer objectSerializer) throws IOException { + this(file, mode, 0L, size, objectSerializer); + } + + public MappedStore(File file, FileChannel.MapMode mode, long startInFile, long size, + ObjectSerializer objectSerializer) throws IOException { + super(new MmapInfoHolder(), file, mode, startInFile, size, objectSerializer); + mmapInfoHolder.lock(); + } +} + diff --git a/lang/src/main/java/net/openhft/lang/io/MultiStoreBytes.java b/lang/src/main/java/net/openhft/lang/io/MultiStoreBytes.java index c2b6679..b9107e0 100644 --- a/lang/src/main/java/net/openhft/lang/io/MultiStoreBytes.java +++ b/lang/src/main/java/net/openhft/lang/io/MultiStoreBytes.java @@ -1,40 +1,57 @@ /* - * Copyright 2013 Peter Lawrey + * Copyright (C) 2015 higherfrequencytrading.com * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as published by + * the Free Software Foundation, either version 3 of the License. * - * http://www.apache.org/licenses/LICENSE-2.0 + * 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 Lesser General Public License for more details. * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. + * You should have received a copy of the GNU Lesser General Public License + * along with this program. If not, see <http://www.gnu.org/licenses/>. */ package net.openhft.lang.io; public class MultiStoreBytes extends NativeBytes { - private DirectStore store; + private Bytes underlyingBytes; + private long underlyingOffset; public MultiStoreBytes() { - super(NO_PAGE, NO_PAGE, NO_PAGE); - this.store = store; + super(NO_PAGE, NO_PAGE); } - public void storePositionAndSize(DirectStore store, long offset, long size) { - if (offset < 0 || size < 0 || offset + size > store.size) - throw new IllegalArgumentException(); - this.store = store; - this.bytesMarshallerFactory = store.bytesMarshallerFactory; - startAddr = positionAddr = store.address + offset; - limitAddr = startAddr + size; + public void storePositionAndSize(BytesStore store, long offset, long size) { + if (offset < 0 || size < 0 || offset + size > store.size()) + throw new IllegalArgumentException("offset: " + offset + ", size: " + size + ", store.size: " + store.size()); + setObjectSerializer(store.objectSerializer()); + + setStartPositionAddress(store.address() + offset); + capacityAddr = limitAddr = startAddr + size; + underlyingBytes = null; + underlyingOffset = 0; + } + + public void setBytesOffset(Bytes bytes, long offset) { + setObjectSerializer(bytes.objectSerializer()); + + long bytesAddr = bytes.address(); + setStartPositionAddress(bytesAddr + offset); + capacityAddr = limitAddr = bytesAddr + bytes.capacity(); + underlyingBytes = bytes; + underlyingOffset = offset; + } + + public Bytes underlyingBytes() { + if (underlyingBytes == null) throw new IllegalStateException(); + return underlyingBytes; } - public DirectStore store() { - return store; + public long underlyingOffset() { + return underlyingOffset; } } diff --git a/lang/src/main/java/net/openhft/lang/io/MutableDecimal.java b/lang/src/main/java/net/openhft/lang/io/MutableDecimal.java index 773ce07..c3037c1 100755..100644 --- a/lang/src/main/java/net/openhft/lang/io/MutableDecimal.java +++ b/lang/src/main/java/net/openhft/lang/io/MutableDecimal.java @@ -1,32 +1,31 @@ /* - * Copyright 2013 Peter Lawrey + * Copyright (C) 2015 higherfrequencytrading.com * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as published by + * the Free Software Foundation, either version 3 of the License. * - * http://www.apache.org/licenses/LICENSE-2.0 + * 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 Lesser General Public License for more details. * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. + * You should have received a copy of the GNU Lesser General Public License + * along with this program. If not, see <http://www.gnu.org/licenses/>. */ package net.openhft.lang.io; -import org.jetbrains.annotations.NotNull; +import net.openhft.lang.model.constraints.NotNull; import java.math.BigDecimal; -import java.math.RoundingMode; /** * @author peter.lawrey */ @SuppressWarnings({"CompareToUsesNonFinalVariable", "NonFinalFieldReferenceInEquals", "NonFinalFieldReferencedInHashCode"}) public class MutableDecimal extends Number implements Comparable<MutableDecimal> { - static final double[] TENS = new double[16]; + private static final double[] TENS = new double[16]; static { TENS[0] = 1; @@ -54,15 +53,13 @@ public class MutableDecimal extends Number implements Comparable<MutableDecimal> set(d, precision); } - public void set(double d, int precision) { - double d2 = precision > 0 ? d * tens(precision) : d / tens(-precision); - scale = precision; - if (Math.abs(d2) < 1e16) { - value = Math.round(d2); - } else { - BigDecimal bd = BigDecimal.valueOf(d).setScale(precision, RoundingMode.HALF_UP); - value = bd.unscaledValue().longValue(); + void set(double d, int precision) { + while (d > Long.MAX_VALUE) { + d /= 10; + precision++; } + value = Math.round(d); + this.scale = precision; } public void set(long value, int scale) { @@ -74,7 +71,7 @@ public class MutableDecimal extends Number implements Comparable<MutableDecimal> return value; } - public int scale() { + int scale() { return scale; } @@ -135,6 +132,7 @@ public class MutableDecimal extends Number implements Comparable<MutableDecimal> if (digit < 0) { digit = 8; v = (v >>> 1) / 5; + } else { v /= 10; } diff --git a/lang/src/main/java/net/openhft/lang/io/NativeBytes.java b/lang/src/main/java/net/openhft/lang/io/NativeBytes.java index 2a974d7..96aba3a 100755 --- a/lang/src/main/java/net/openhft/lang/io/NativeBytes.java +++ b/lang/src/main/java/net/openhft/lang/io/NativeBytes.java @@ -1,28 +1,35 @@ /* - * Copyright 2013 Peter Lawrey + * Copyright (C) 2015 higherfrequencytrading.com * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as published by + * the Free Software Foundation, either version 3 of the License. * - * http://www.apache.org/licenses/LICENSE-2.0 + * 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 Lesser General Public License for more details. * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. + * You should have received a copy of the GNU Lesser General Public License + * along with this program. If not, see <http://www.gnu.org/licenses/>. */ package net.openhft.lang.io; import net.openhft.lang.io.serialization.BytesMarshallerFactory; +import net.openhft.lang.io.serialization.ObjectSerializer; import org.jetbrains.annotations.NotNull; import sun.misc.Unsafe; import java.io.EOFException; +import java.io.IOException; import java.lang.reflect.Field; +import java.nio.BufferUnderflowException; +import java.nio.ByteBuffer; import java.nio.ByteOrder; +import java.util.concurrent.atomic.AtomicInteger; + +import static java.lang.Long.numberOfTrailingZeros; /** * @author peter.lawrey @@ -34,7 +41,9 @@ public class NativeBytes extends AbstractBytes { @NotNull @SuppressWarnings("ALL") public static final Unsafe UNSAFE; + protected static final long NO_PAGE; static final int BYTES_OFFSET; + static final int CHARS_OFFSET; static { try { @@ -43,36 +52,64 @@ public class NativeBytes extends AbstractBytes { theUnsafe.setAccessible(true); UNSAFE = (Unsafe) theUnsafe.get(null); BYTES_OFFSET = UNSAFE.arrayBaseOffset(byte[].class); - + CHARS_OFFSET = UNSAFE.arrayBaseOffset(char[].class); } catch (Exception e) { throw new AssertionError(e); } + NO_PAGE = UNSAFE.allocateMemory(UNSAFE.pageSize()); } - protected static final long NO_PAGE = UNSAFE.allocateMemory(UNSAFE.pageSize()); - protected long startAddr; protected long positionAddr; protected long limitAddr; + protected long capacityAddr; - public NativeBytes(long startAddr, long positionAddr, long limitAddr) { - this.startAddr = startAddr; - this.positionAddr = positionAddr; - this.limitAddr = limitAddr; + public NativeBytes(long startAddr, long capacityAddr) { + super(); + setStartPositionAddress(startAddr); + if (startAddr > capacityAddr) + throw new IllegalArgumentException("Missorted capacity address"); + this.limitAddr = + this.capacityAddr = capacityAddr; + positionChecks(positionAddr); } - public NativeBytes(BytesMarshallerFactory bytesMarshallerFactory, long startAddr, long positionAddr, long limitAddr) { - super(bytesMarshallerFactory); - this.startAddr = startAddr; - this.positionAddr = positionAddr; - this.limitAddr = limitAddr; + /** + * @deprecated Use {@link #NativeBytes(ObjectSerializer, long, long, AtomicInteger)} instead + */ + @Deprecated + public NativeBytes(BytesMarshallerFactory bytesMarshallerFactory, + long startAddr, long capacityAddr, AtomicInteger refCount) { + super(bytesMarshallerFactory, refCount); + + setStartPositionAddress(startAddr); + this.limitAddr = + this.capacityAddr = capacityAddr; + positionChecks(positionAddr); + } + + public NativeBytes(ObjectSerializer objectSerializer, + long startAddr, long capacityAddr, AtomicInteger refCount) { + super(objectSerializer, refCount); + + setStartPositionAddress(startAddr); + this.limitAddr = + this.capacityAddr = capacityAddr; + positionChecks(positionAddr); } public NativeBytes(NativeBytes bytes) { - super(bytes.bytesMarshallerFactory()); - this.startAddr = bytes.startAddr; + super(bytes.objectSerializer(), new AtomicInteger(1)); + setStartPositionAddress(bytes.startAddr); + this.positionAddr = bytes.positionAddr; this.limitAddr = bytes.limitAddr; + this.capacityAddr = bytes.capacityAddr; + positionChecks(positionAddr); + } + + public static NativeBytes wrap(long address, long capacity) { + return new NativeBytes(address, address + capacity); } public static long longHash(byte[] bytes, int off, int len) { @@ -85,6 +122,177 @@ public class NativeBytes extends AbstractBytes { return hash; } + static long nextSetBit0(int firstByte, int maximum, long startAddr) { + for (int i = firstByte; i < maximum; i += 8) { + long l = UNSAFE.getLong(startAddr + i); + if (l != 0) + return (i << 3) + numberOfTrailingZeros(l); + } + return -1; + } + + static long nextSetBit0(long firstByte, long maximum, long startAddr) { + for (long i = firstByte; i < maximum; i += 8) { + long l = UNSAFE.getLong(startAddr + i); + if (l != 0) + return (i << 3) + numberOfTrailingZeros(l); + } + return -1; + } + + public void setStartPositionAddress(long startAddr) { + if ((startAddr & ~0x3fff) == 0) + throw new AssertionError("Invalid address " + Long.toHexString(startAddr)); + this.positionAddr = + this.startAddr = startAddr; + } + + // optimised to reduce overhead. + public void readUTF0(@NotNull Appendable appendable, int utflen) + throws IOException { + if (utflen > remaining()) + throw new BufferUnderflowException(); + if (appendable instanceof StringBuilder) + readUTF1((StringBuilder) appendable, utflen); + else + readUTF1(appendable, utflen); + } + + private void readUTF1(@NotNull StringBuilder sb, int utflen) + throws IOException { + int count = 0; + sb.ensureCapacity(utflen); + char[] chars = StringBuilderUtils.extractChars(sb); + ascii: + try { + while (count < utflen) { + int c = UNSAFE.getByte(positionAddr++) & 0xFF; + if (c >= 128) { + break ascii; + } + chars[count++] = (char) c; + } + return; + } finally { + StringBuilderUtils.setCount(sb, count); + } + + positionAddr--; + readUTF2(this, sb, utflen, count); + } + + private void readUTF1(@NotNull Appendable appendable, int utflen) + throws IOException { + int count = 0; + while (count < utflen) { + int c = UNSAFE.getByte(positionAddr++) & 0xFF; + if (c >= 128) { + positionAddr--; + readUTF2(this, appendable, utflen, count); + break; + } + count++; + appendable.append((char) c); + } + } + + @Override + public NativeBytes slice() { + return new NativeBytes(objectSerializer(), positionAddr, limitAddr, refCount); + } + + @Override + public NativeBytes slice(long offset, long length) { + long sliceStart = positionAddr + offset; + assert sliceStart >= startAddr && sliceStart < capacityAddr; + long sliceEnd = sliceStart + length; + assert sliceEnd > sliceStart && sliceEnd <= capacityAddr; + return new NativeBytes(objectSerializer(), sliceStart, sliceEnd, refCount); + } + + @Override + public CharSequence subSequence(int start, int end) { + long subStart = positionAddr + start; + if (subStart < positionAddr || subStart > limitAddr) + throw new IndexOutOfBoundsException(); + long subEnd = positionAddr + end; + if (subEnd < subStart || subEnd > limitAddr) + throw new IndexOutOfBoundsException(); + if (start == end) + return ""; + return new NativeBytes(objectSerializer(), subStart, subEnd, refCount); + } + + @Override + public NativeBytes bytes() { + return new NativeBytes(objectSerializer(), startAddr, capacityAddr, refCount); + } + + @Override + public NativeBytes bytes(long offset, long length) { + long sliceStart = startAddr + offset; + assert sliceStart >= startAddr && sliceStart < capacityAddr; + long sliceEnd = sliceStart + length; + assert sliceEnd > sliceStart && sliceEnd <= capacityAddr; + return new NativeBytes(objectSerializer(), sliceStart, sliceEnd, refCount); + } + + @Override + public long address() { + return startAddr; + } + + @Override + public Bytes zeroOut() { + clear(); + UNSAFE.setMemory(startAddr, capacity(), (byte) 0); + return this; + } + + @Override + public Bytes zeroOut(long start, long end) { + if (start < 0 || end > limit()) + throw new IllegalArgumentException("start: " + start + ", end: " + end); + if (start >= end) + return this; + UNSAFE.setMemory(startAddr + start, end - start, (byte) 0); + return this; + } + + @Override + public Bytes zeroOut(long start, long end, boolean ifNotZero) { + return ifNotZero ? zeroOutDirty(start, end) : zeroOut(start, end); + } + + private Bytes zeroOutDirty(long start, long end) { + if (start < 0 || end > limit()) + throw new IllegalArgumentException("start: " + start + ", end: " + end); + if (start >= end) + return this; + // get unaligned leading bytes + while (start < end && (start & 7) != 0) { + byte b = UNSAFE.getByte(startAddr + start); + if (b != 0) + UNSAFE.putByte(startAddr + start, (byte) 0); + start++; + } + // check 64-bit aligned access + while (start < end - 7) { + long l = UNSAFE.getLong(startAddr + start); + if (l != 0) + UNSAFE.putLong(startAddr + start, 0L); + start += 8; + } + // check unaligned tail + while (start < end) { + byte b = UNSAFE.getByte(startAddr + start); + if (b != 0) + UNSAFE.putByte(startAddr + start, (byte) 0); + start++; + } + return this; + } + @Override public int read(@NotNull byte[] bytes, int off, int len) { if (len < 0 || off < 0 || off + len > bytes.length) @@ -93,13 +301,15 @@ public class NativeBytes extends AbstractBytes { if (left <= 0) return -1; int len2 = (int) Math.min(len, left); UNSAFE.copyMemory(null, positionAddr, bytes, BYTES_OFFSET + off, len2); - positionAddr += len2; + addPosition(len2); return len2; } @Override public byte readByte() { - return UNSAFE.getByte(positionAddr++); + byte aByte = UNSAFE.getByte(positionAddr); + addPosition(1); + return aByte; } @Override @@ -109,19 +319,36 @@ public class NativeBytes extends AbstractBytes { @Override public void readFully(@NotNull byte[] b, int off, int len) { - if (len < 0 || off < 0 || off + len > b.length) - throw new IllegalArgumentException(); + checkArrayOffs(b.length, off, len); long left = remaining(); if (left < len) throw new IllegalStateException(new EOFException()); UNSAFE.copyMemory(null, positionAddr, b, BYTES_OFFSET + off, len); - positionAddr += len; + addPosition(len); + } + + @Override + public void readFully(long offset, byte[] bytes, int off, int len) { + checkArrayOffs(bytes.length, off, len); + UNSAFE.copyMemory(null, startAddr + offset, bytes, BYTES_OFFSET + off, len); + } + + @Override + public void readFully(@NotNull char[] data, int off, int len) { + checkArrayOffs(data.length, off, len); + long bytesOff = off * 2L; + long bytesLen = len * 2L; + long left = remaining(); + if (left < bytesLen) + throw new IllegalStateException(new EOFException()); + UNSAFE.copyMemory(null, positionAddr, data, BYTES_OFFSET + bytesOff, bytesLen); + addPosition(bytesLen); } @Override public short readShort() { short s = UNSAFE.getShort(positionAddr); - positionAddr += 2; + addPosition(2); return s; } @@ -133,7 +360,7 @@ public class NativeBytes extends AbstractBytes { @Override public char readChar() { char ch = UNSAFE.getChar(positionAddr); - positionAddr += 2; + addPosition(2); return ch; } @@ -145,7 +372,7 @@ public class NativeBytes extends AbstractBytes { @Override public int readInt() { int i = UNSAFE.getInt(positionAddr); - positionAddr += 4; + addPosition(4); return i; } @@ -157,7 +384,7 @@ public class NativeBytes extends AbstractBytes { @Override public int readVolatileInt() { int i = UNSAFE.getIntVolatile(null, positionAddr); - positionAddr += 4; + addPosition(4); return i; } @@ -169,7 +396,7 @@ public class NativeBytes extends AbstractBytes { @Override public long readLong() { long l = UNSAFE.getLong(positionAddr); - positionAddr += 8; + addPosition(8); return l; } @@ -181,7 +408,7 @@ public class NativeBytes extends AbstractBytes { @Override public long readVolatileLong() { long l = UNSAFE.getLongVolatile(null, positionAddr); - positionAddr += 8; + addPosition(8); return l; } @@ -193,7 +420,7 @@ public class NativeBytes extends AbstractBytes { @Override public float readFloat() { float f = UNSAFE.getFloat(positionAddr); - positionAddr += 4; + addPosition(4); return f; } @@ -205,7 +432,7 @@ public class NativeBytes extends AbstractBytes { @Override public double readDouble() { double d = UNSAFE.getDouble(positionAddr); - positionAddr += 8; + addPosition(8); return d; } @@ -216,11 +443,13 @@ public class NativeBytes extends AbstractBytes { @Override public void write(int b) { - UNSAFE.putByte(positionAddr++, (byte) b); + UNSAFE.putByte(positionAddr, (byte) b); + incrementPositionAddr(1); } @Override public void writeByte(long offset, int b) { + offsetChecks(offset, 1L); UNSAFE.putByte(startAddr + offset, (byte) b); } @@ -229,124 +458,163 @@ public class NativeBytes extends AbstractBytes { if (offset < 0 || offset + bytes.length > capacity()) throw new IllegalArgumentException(); UNSAFE.copyMemory(bytes, BYTES_OFFSET, null, startAddr + offset, bytes.length); - positionAddr += bytes.length; + addPosition(bytes.length); } @Override public void write(byte[] bytes, int off, int len) { + if (off < 0 || off + len > bytes.length || len > remaining()) + throw new IllegalArgumentException(); UNSAFE.copyMemory(bytes, BYTES_OFFSET + off, null, positionAddr, len); - positionAddr += len; + addPosition(len); + } + + @Override + public void write(long offset, byte[] bytes, int off, int len) { + if (offset < 0 || off + len > bytes.length || offset + len > capacity()) + throw new IllegalArgumentException(); + UNSAFE.copyMemory(bytes, BYTES_OFFSET + off, null, startAddr + offset, len); } @Override public void writeShort(int v) { + positionChecks(positionAddr + 2L); UNSAFE.putShort(positionAddr, (short) v); - positionAddr += 2; + positionAddr += 2L; + } + + private long incrementPositionAddr(long value) { + positionAddr(positionAddr() + value); + return positionAddr(); } @Override public void writeShort(long offset, int v) { + offsetChecks(offset, 2L); UNSAFE.putShort(startAddr + offset, (short) v); } @Override public void writeChar(int v) { + positionChecks(positionAddr + 2L); UNSAFE.putChar(positionAddr, (char) v); - positionAddr += 2; + positionAddr += 2L; + } + + void addPosition(long delta) { + positionAddr(positionAddr() + delta); } @Override public void writeChar(long offset, int v) { + offsetChecks(offset, 2L); UNSAFE.putChar(startAddr + offset, (char) v); } @Override public void writeInt(int v) { - UNSAFE.putInt(null, positionAddr, v); - positionAddr += 4; + positionChecks(positionAddr + 4L); + UNSAFE.putInt(positionAddr, v); + positionAddr += 4L; } @Override public void writeInt(long offset, int v) { + offsetChecks(offset, 4L); UNSAFE.putInt(startAddr + offset, v); } @Override public void writeOrderedInt(int v) { + positionChecks(positionAddr + 4L); UNSAFE.putOrderedInt(null, positionAddr, v); - positionAddr += 4; + positionAddr += 4L; } @Override public void writeOrderedInt(long offset, int v) { + offsetChecks(offset, 4L); UNSAFE.putOrderedInt(null, startAddr + offset, v); } @Override public boolean compareAndSwapInt(long offset, int expected, int x) { + offsetChecks(offset, 4L); return UNSAFE.compareAndSwapInt(null, startAddr + offset, expected, x); } @Override public void writeLong(long v) { + positionChecks(positionAddr + 8L); UNSAFE.putLong(positionAddr, v); - positionAddr += 8; + positionAddr += 8L; } @Override public void writeLong(long offset, long v) { + offsetChecks(offset, 8L); UNSAFE.putLong(startAddr + offset, v); } @Override public void writeOrderedLong(long v) { + positionChecks(positionAddr + 8L); UNSAFE.putOrderedLong(null, positionAddr, v); - positionAddr += 8; + positionAddr += 8L; } @Override public void writeOrderedLong(long offset, long v) { + offsetChecks(offset, 8L); UNSAFE.putOrderedLong(null, startAddr + offset, v); } @Override public boolean compareAndSwapLong(long offset, long expected, long x) { + offsetChecks(offset, 8L); return UNSAFE.compareAndSwapLong(null, startAddr + offset, expected, x); } @Override public void writeFloat(float v) { + positionChecks(positionAddr + 4L); UNSAFE.putFloat(positionAddr, v); - positionAddr += 4; + positionAddr += 4L; } @Override public void writeFloat(long offset, float v) { + offsetChecks(offset, 4L); UNSAFE.putFloat(startAddr + offset, v); } @Override public void writeDouble(double v) { + positionChecks(positionAddr + 8L); UNSAFE.putDouble(positionAddr, v); - positionAddr += 8; + positionAddr += 8L; } @Override public void writeDouble(long offset, double v) { + offsetChecks(offset, 8L); UNSAFE.putDouble(startAddr + offset, v); } @Override public void readObject(Object object, int start, int end) { int len = end - start; + if (positionAddr + len >= limitAddr) + throw new IndexOutOfBoundsException("Length out of bounds len: " + len); + for (; len >= 8; len -= 8) { UNSAFE.putLong(object, (long) start, UNSAFE.getLong(positionAddr)); - positionAddr += 8; + incrementPositionAddr(8L); start += 8; } for (; len > 0; len--) { UNSAFE.putByte(object, (long) start, UNSAFE.getByte(positionAddr)); - positionAddr++; + incrementPositionAddr(1L); start++; } } @@ -354,12 +622,15 @@ public class NativeBytes extends AbstractBytes { @Override public void writeObject(Object object, int start, int end) { int len = end - start; + for (; len >= 8; len -= 8) { + positionChecks(positionAddr + 8L); UNSAFE.putLong(positionAddr, UNSAFE.getLong(object, (long) start)); positionAddr += 8; start += 8; } for (; len > 0; len--) { + positionChecks(positionAddr + 1L); UNSAFE.putByte(positionAddr, UNSAFE.getByte(object, (long) start)); positionAddr++; start++; @@ -367,18 +638,82 @@ public class NativeBytes extends AbstractBytes { } @Override + public boolean compare(long offset, RandomDataInput input, long inputOffset, long len) { + if (offset < 0 || inputOffset < 0 || len < 0) + throw new IndexOutOfBoundsException(); + if (offset + len < 0 || offset + len > capacity() || inputOffset + len < 0 || + inputOffset + len > input.capacity()) { + return false; + } + long i = 0L; + for (; i < len - 7L; i += 8L) { + if (UNSAFE.getLong(startAddr + offset + i) != input.readLong(inputOffset + i)) + return false; + } + if (i < len - 3L) { + if (UNSAFE.getInt(startAddr + offset + i) != input.readInt(inputOffset + i)) + return false; + i += 4L; + } + if (i < len - 1L) { + if (UNSAFE.getChar(startAddr + offset + i) != input.readChar(inputOffset + i)) + return false; + i += 2L; + } + if (i < len) { + if (UNSAFE.getByte(startAddr + offset + i) != input.readByte(inputOffset + i)) + return false; + } + return true; + } + + @Override public long position() { return (positionAddr - startAddr); } @Override - public void position(long position) { - this.positionAddr = startAddr + position; + public NativeBytes position(long position) { + if (position < 0 || position > limit()) + throw new IndexOutOfBoundsException("position: " + position + " limit: " + limit()); + + positionAddr(startAddr + position); + return this; + } + + /** + * Change the position acknowleging there is no thread safety assumptions. Best effort setting + * is fine. * + * + * @param position to set if we can. + * @return this + */ + public NativeBytes lazyPosition(long position) { + if (position < 0 || position > limit()) + throw new IndexOutOfBoundsException("position: " + position + " limit: " + limit()); + + // assume we don't need to no check thread safety. + + positionAddr(startAddr + position); + return this; + } + + @Override + public void write(RandomDataInput bytes, long position, long length) { + if (length > remaining()) + throw new IllegalArgumentException("Attempt to write " + length + " bytes with " + remaining() + " remaining"); + if (bytes instanceof NativeBytes) { + UNSAFE.copyMemory(((NativeBytes) bytes).startAddr + position, positionAddr, length); + skip(length); + + } else { + super.write(bytes, position, length); + } } @Override public long capacity() { - return (limitAddr - startAddr); + return (capacityAddr - startAddr); } @Override @@ -386,6 +721,21 @@ public class NativeBytes extends AbstractBytes { return (limitAddr - positionAddr); } + @Override + public long limit() { + return (limitAddr - startAddr); + } + + @Override + public NativeBytes limit(long limit) { + if (limit < 0 || limit > capacity()) { + throw new IllegalArgumentException("limit: " + limit + " capacity: " + capacity()); + } + + limitAddr = startAddr + limit; + return this; + } + @NotNull @Override public ByteOrder byteOrder() { @@ -394,23 +744,107 @@ public class NativeBytes extends AbstractBytes { @Override public void checkEndOfBuffer() throws IndexOutOfBoundsException { - if (position() > capacity()) - throw new IndexOutOfBoundsException("position is beyond the end of the buffer " + position() + " > " + capacity()); + if (position() > limit()) { + throw new IndexOutOfBoundsException( + "position is beyond the end of the buffer " + position() + " > " + limit()); + } } public long startAddr() { return startAddr; } - public long positionAddr() { - return positionAddr; + long capacityAddr() { + return capacityAddr; + } + + @Override + protected void cleanup() { + // TODO nothing to do. + } + + @Override + public Bytes load() { + int pageSize = UNSAFE.pageSize(); + for (long addr = startAddr; addr < capacityAddr; addr += pageSize) + UNSAFE.getByte(addr); + return this; + } + + public void alignPositionAddr(int powerOf2) { + long value = (positionAddr + powerOf2 - 1) & ~(powerOf2 - 1); + positionAddr(value); } public void positionAddr(long positionAddr) { + positionChecks(positionAddr); this.positionAddr = positionAddr; } - public long limitAddr() { - return limitAddr; + void positionChecks(long positionAddr) { + assert actualPositionChecks(positionAddr); + } + + boolean actualPositionChecks(long positionAddr) { + if (positionAddr < startAddr) + throw new IndexOutOfBoundsException("position before the start by " + (startAddr - positionAddr) + " bytes"); + if (positionAddr > limitAddr) + throw new IndexOutOfBoundsException("position after the limit by " + (positionAddr - limitAddr) + " bytes"); + + return true; + } + + void offsetChecks(long offset, long len) { + assert actualOffsetChecks(offset, len); + } + + boolean actualOffsetChecks(long offset, long len) { + if (offset < 0L || offset + len > capacity()) + throw new IndexOutOfBoundsException("offset out of bounds: " + offset + ", len: " + + len + ", capacity: " + capacity()); + return true; + } + + public long positionAddr() { + return positionAddr; + } + + @Override + public ByteBuffer sliceAsByteBuffer(ByteBuffer toReuse) { + return sliceAsByteBuffer(toReuse, null); + } + + protected ByteBuffer sliceAsByteBuffer(ByteBuffer toReuse, Object att) { + return ByteBufferReuse.INSTANCE.reuse(positionAddr, (int) remaining(), att, toReuse); + } + + void address(long address) { + setStartPositionAddress(address); + } + + void capacity(long capacity) { + this.limitAddr = this.capacityAddr = capacity; + } + + @Override + public long nextSetBit(long fromIndex) { + if (fromIndex < 0) + throw new IndexOutOfBoundsException(); + long capacity = capacity(); + long maxBit = capacity << 3; + long fromLongIndex = fromIndex & ~63; + if (fromLongIndex >= maxBit) + return -1; + long firstByte = fromLongIndex >>> 3; + if ((fromIndex & 63) != 0) { + long l = UNSAFE.getLongVolatile(null, startAddr + firstByte) >>> fromIndex; + if (l != 0) { + return fromIndex + numberOfTrailingZeros(l); + } + firstByte += 8; + } + if (capacity < Integer.MAX_VALUE) + return nextSetBit0((int) firstByte, (int) capacity, startAddr); + return nextSetBit0(firstByte, capacity, startAddr); } } diff --git a/lang/src/main/java/net/openhft/lang/io/RandomDataInput.java b/lang/src/main/java/net/openhft/lang/io/RandomDataInput.java index 3581b0f..10f97e3 100755 --- a/lang/src/main/java/net/openhft/lang/io/RandomDataInput.java +++ b/lang/src/main/java/net/openhft/lang/io/RandomDataInput.java @@ -1,17 +1,17 @@ /* - * Copyright 2013 Peter Lawrey + * Copyright (C) 2015 higherfrequencytrading.com * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as published by + * the Free Software Foundation, either version 3 of the License. * - * http://www.apache.org/licenses/LICENSE-2.0 + * 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 Lesser General Public License for more details. * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. + * You should have received a copy of the GNU Lesser General Public License + * along with this program. If not, see <http://www.gnu.org/licenses/>. */ package net.openhft.lang.io; @@ -20,6 +20,7 @@ import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.Nullable; import java.io.ObjectInput; +import java.io.StreamCorruptedException; import java.nio.ByteBuffer; import java.util.Collection; import java.util.Map; @@ -30,21 +31,22 @@ import java.util.RandomAccess; */ public interface RandomDataInput extends ObjectInput, RandomAccess, BytesCommon { /** - * Reads some bytes from an input stream and stores them into the buffer array <code>b</code>. The number of bytes + * <p>Reads some bytes from an input stream and stores them into the buffer array <code>b</code>. The number of bytes * read is equal to the length of <code>b</code>. - * <p/> - * This method blocks until one of the following conditions occurs:<p> <ul> <li><code>b.length</code> bytes of input - * data are available, in which case a normal return is made. - * <p/> - * <li>End of file is detected, in which case an <code>EOFException</code> is thrown. - * <p/> - * <li>An I/O error occurs, in which case an <code>IOException</code> other than <code>EOFException</code> is - * thrown. </ul> - * <p/> - * If <code>b</code> is <code>null</code>, a <code>NullPointerException</code> is thrown. If <code>b.length</code> - * is zero, then no bytes are read. Otherwise, the first byte read is stored into element <code>b[0]</code>, the - * next one into <code>b[1]</code>, and so on. If an exception is thrown from this method, then it may be that some - * but not all bytes of <code>b</code> have been updated with data from the input stream. + * </p><p> + * This method blocks until one of the following conditions occurs: + * </p> + * <ul> + * <li><code>b.length</code> bytes of input data are available, in which case a normal return is made. </li> + * <li>End of file is detected, in which case an <code>EOFException</code> is thrown.</li> + * <li>An I/O error occurs, in which case an <code>IOException</code> other than <code>EOFException</code> is thrown.</li> + * </ul> + * <p> + * If <code>bytes</code> is <code>null</code>, a <code>NullPointerException</code> is thrown. If <code>bytes.length</code> + * is zero, then no bytes are read. Otherwise, the first byte read is stored into element <code>bytes[0]</code>, the + * next one into <code>bytes[1]</code>, and so on. If an exception is thrown from this method, then it may be that some + * but not all bytes of <code>bytes</code> have been updated with data from the input stream. + * </p> * * @param bytes the buffer into which the data is read. */ @@ -52,21 +54,22 @@ public interface RandomDataInput extends ObjectInput, RandomAccess, BytesCommon void readFully(@NotNull byte[] bytes); /** - * Reads <code>len</code> bytes from an input stream. - * <p/> - * This method blocks until one of the following conditions occurs:<p> <ul> <li><code>len</code> bytes of input data - * are available, in which case a normal return is made. - * <p/> - * <li>End of file is detected, in which case an <code>EOFException</code> is thrown. - * <p/> - * <li>An I/O error occurs, in which case an <code>IOException</code> other than <code>EOFException</code> is - * thrown. </ul> - * <p/> - * If <code>b</code> is <code>null</code>, a <code>NullPointerException</code> is thrown. If <code>off</code> is + * <p>Reads <code>len</code> bytes from an input stream. + * </p><p> + * This method blocks until one of the following conditions occurs: + * </p> + * <ul> + * <li><code>len</code> bytes of input data are available, in which case a normal return is made.</li> + * <li>End of file is detected, in which case an <code>EOFException</code> is thrown.</li> + * <li>An I/O error occurs, in which case an <code>IOException</code> other than <code>EOFException</code> is thrown. </li> + * </ul> + * <p> + * If <code>bytes</code> is <code>null</code>, a <code>NullPointerException</code> is thrown. If <code>off</code> is * negative, or <code>len</code> is negative, or <code>off+len</code> is greater than the length of the array - * <code>b</code>, then an <code>IndexOutOfBoundsException</code> is thrown. If <code>len</code> is zero, then no - * bytes are read. Otherwise, the first byte read is stored into element <code>b[off]</code>, the next one into - * <code>b[off+1]</code>, and so on. The number of bytes read is, at most, equal to <code>len</code>. + * <code>bytes</code>, then an <code>IndexOutOfBoundsException</code> is thrown. If <code>len</code> is zero, then no + * bytes are read. Otherwise, the first byte read is stored into element <code>bytes[off]</code>, the next one into + * <code>bytes[off+1]</code>, and so on. The number of bytes read is, at most, equal to <code>len</code>. + * </p> * * @param bytes the buffer into which the data is read. * @param off an int specifying the offset into the data. @@ -75,6 +78,12 @@ public interface RandomDataInput extends ObjectInput, RandomAccess, BytesCommon @Override void readFully(@NotNull byte[] bytes, int off, int len); + void readFully(long offset, @NotNull byte[] bytes, int off, int len); + + void readFully(@NotNull char[] data); + + void readFully(@NotNull char[] data, int off, int len); + /** * Makes an attempt to skip over <code>n</code> bytes of data from the input stream, discarding the skipped bytes. * However, it may skip over some smaller number of bytes, possibly zero. This may result from any of a number of @@ -153,7 +162,7 @@ public interface RandomDataInput extends ObjectInput, RandomAccess, BytesCommon * Reads two input bytes and returns a <code>short</code> value. Let <code>a</code> be the first byte read and * <code>b</code> be the second byte on big endian machines, and the opposite on little endian machines. The value * returned is: - * <p><pre><code>(short)((a << 8) | (b & 0xff)) + * <pre><code>(short)((a << 8) | (b & 0xff)) * </code></pre> * This method is suitable for reading the bytes written by the <code>writeShort</code> method of interface * <code>DataOutput</code>. @@ -167,7 +176,7 @@ public interface RandomDataInput extends ObjectInput, RandomAccess, BytesCommon * Reads two input bytes and returns a <code>short</code> value. Let <code>a</code> be the first byte read and * <code>b</code> be the second byte on big endian machines, and the opposite on little endian machines. The value * returned is: - * <p><pre><code> + * <pre><code> * (short)((a << 8) | (b & 0xff)) * </code></pre> * This method is suitable for reading the bytes written by the <code>writeShort</code> method of interface @@ -182,7 +191,7 @@ public interface RandomDataInput extends ObjectInput, RandomAccess, BytesCommon * Reads two input bytes and returns an <code>int</code> value in the range <code>0</code> through * <code>65535</code>. Let <code>a</code> be the first byte read and <code>b</code> be the second byte on big endian * machines, and the opposite on little endian machines. The value returned is: - * <p><pre><code> + * <pre><code> * (((a & 0xff) << 8) | (b & 0xff)) * </code></pre> * This method is suitable for reading the bytes written by the <code>writeUnsignedShort</code> method of interface @@ -198,7 +207,7 @@ public interface RandomDataInput extends ObjectInput, RandomAccess, BytesCommon * Reads two input bytes and returns an <code>int</code> value in the range <code>0</code> through * <code>65535</code>. Let <code>a</code> be the first byte read and <code>b</code> be the second byte on big endian * machines, and the opposite on little endian machines. The value returned is: - * <p><pre><code> + * <pre><code> * (((a & 0xff) << 8) | (b & 0xff)) * </code></pre> * This method is suitable for reading the bytes written by the <code>writeShort</code> method of interface @@ -214,8 +223,8 @@ public interface RandomDataInput extends ObjectInput, RandomAccess, BytesCommon * Reads one or three input bytes and returns a <code>short</code> value. Let <code>a</code> be the first byte read. * This mapped as follows; Byte.MIN_VALUE => Short.MIN_VALUE, Byte.MAX_VALUE => Short.MAX_VALUE, Byte.MIN_VALUE+2 to * Byte.MAX_VALUE-1 => same as short value, Byte.MIN_VALUE+1 => readShort(). - * <p/> - * This method is suitable for reading the bytes written by the <code>writeCompactShort</code> method of interface + * + * <p>This method is suitable for reading the bytes written by the <code>writeCompactShort</code> method of interface * <code>RandomDataOutput</code>. * * @return the 16-bit value read. @@ -225,8 +234,8 @@ public interface RandomDataInput extends ObjectInput, RandomAccess, BytesCommon /** * Reads one or three input bytes and returns a <code>short</code> value. Let <code>a</code> be the first byte read. * This mapped as follows; -1 => readUnsignedShort(), default => (a & 0xFF) - * <p/> - * This method is suitable for reading the bytes written by the <code>writeCompactUnsignedShort</code> method of + * + * <p>This method is suitable for reading the bytes written by the <code>writeCompactUnsignedShort</code> method of * interface <code>RandomDataOutput</code>. * * @return the unsigned 16-bit value read. @@ -237,7 +246,7 @@ public interface RandomDataInput extends ObjectInput, RandomAccess, BytesCommon * Reads two input bytes and returns a <code>char</code> value. Let <code>a</code> be the first byte read and * <code>b</code> be the second byte on big endian machines, and the opposite on little endian machines. The value * returned is: - * <p><pre><code>(char)((a << 8) | (b & 0xff)) + * <pre><code>(char)((a << 8) | (b & 0xff)) * </code></pre> * This method is suitable for reading bytes written by the <code>writeChar</code> method of interface * <code>DataOutput</code>. @@ -251,7 +260,7 @@ public interface RandomDataInput extends ObjectInput, RandomAccess, BytesCommon * Reads two input bytes and returns a <code>char</code> value. Let <code>a</code> be the first byte read and * <code>b</code> be the second byte on big endian machines, and the opposite on little endian machines. The value * returned is: - * <p><pre><code>(char)((a << 8) | (b & 0xff)) + * <pre><code>(char)((a << 8) | (b & 0xff)) * </code></pre> * This method is suitable for reading bytes written by the <code>writeChar</code> method of interface * <code>RandomDataOutput</code>. @@ -264,7 +273,7 @@ public interface RandomDataInput extends ObjectInput, RandomAccess, BytesCommon /** * Reads three input bytes and returns a 24-bit <code>int</code> value. Let <code>a-c</code> be the first through * third bytes read on big endian machines, and the opposite on little endian machines. The value returned is: - * <p><pre> + * <pre> * <code> * ((((a & 0xff) << 24) | ((b & 0xff) << 16) | ((c & 0xff) << 8))) >> 8 * </code></pre> @@ -278,7 +287,7 @@ public interface RandomDataInput extends ObjectInput, RandomAccess, BytesCommon /** * Reads three input bytes and returns a 24-bit <code>int</code> value. Let <code>a-c</code> be the first through * third bytes read on big endian machines, and the opposite on little endian machines. The value returned is: - * <p><pre> + * <pre> * <code> * ((((a & 0xff) << 24) | ((b & 0xff) << 16) | ((c & 0xff) << 8))) >> 8 * </code></pre> @@ -293,7 +302,7 @@ public interface RandomDataInput extends ObjectInput, RandomAccess, BytesCommon /** * Reads four input bytes and returns an <code>int</code> value. Let <code>a-d</code> be the first through fourth * bytes read on big endian machines, and the opposite on little endian machines. The value returned is: - * <p><pre> + * <pre> * <code> * (((a & 0xff) << 24) | ((b & 0xff) << 16) | ((c & 0xff) << 8) | (d & 0xff)) * </code></pre> @@ -308,7 +317,7 @@ public interface RandomDataInput extends ObjectInput, RandomAccess, BytesCommon /** * Reads four input bytes and returns an <code>int</code> value. Let <code>a-d</code> be the first through fourth * bytes read on big endian machines, and the opposite on little endian machines. The value returned is: - * <p><pre> + * <pre> * <code> * (((a & 0xff) << 24) | ((b & 0xff) << 16) | ((c & 0xff) << 8) | (d & 0xff)) * </code></pre> @@ -324,7 +333,7 @@ public interface RandomDataInput extends ObjectInput, RandomAccess, BytesCommon * This is the same as readInt() except a read barrier is performed first. <p> Reads four input bytes and returns * an <code>int</code> value. Let <code>a-d</code> be the first through fourth bytes read on big endian machines, * and the opposite on little endian machines. The value returned is: - * <p><pre> + * <pre> * <code> * (((a & 0xff) << 24) | ((b & 0xff) << 16) | ((c & 0xff) << 8) | (d & 0xff)) * </code></pre> @@ -339,7 +348,7 @@ public interface RandomDataInput extends ObjectInput, RandomAccess, BytesCommon * This is the same as readInt() except a read barrier is performed first. <p> Reads four input bytes and returns * an <code>int</code> value. Let <code>a-d</code> be the first through fourth bytes read on big endian machines, * and the opposite on little endian machines. The value returned is: - * <p><pre> + * <pre> * <code> * (((a & 0xff) << 24) | ((b & 0xff) << 16) | ((c & 0xff) << 8) | (d & 0xff)) * </code></pre> @@ -354,7 +363,7 @@ public interface RandomDataInput extends ObjectInput, RandomAccess, BytesCommon /** * Reads four input bytes and returns an <code>int</code> value. Let <code>a-d</code> be the first through fourth * bytes read on big endian machines, and the opposite on little endian machines. The value returned is: - * <p><pre> + * <pre> * <code> * ((((long) a & 0xff) << 24) | ((b & 0xff) << 16) | ((c & 0xff) << 8) | (d & * 0xff)) @@ -369,7 +378,7 @@ public interface RandomDataInput extends ObjectInput, RandomAccess, BytesCommon /** * Reads four input bytes and returns an <code>int</code> value. Let <code>a-d</code> be the first through fourth * bytes read on big endian machines, and the opposite on little endian machines. The value returned is: - * <p><pre> + * <pre> * <code> * ((((long) a & 0xff) << 24) | ((b & 0xff) << 16) | ((c & 0xff) << 8) | (d & * 0xff)) @@ -387,8 +396,8 @@ public interface RandomDataInput extends ObjectInput, RandomAccess, BytesCommon * with readShort(). This mapped as follows; Short.MIN_VALUE => Integer.MIN_VALUE, Short.MAX_VALUE => * Integer.MAX_VALUE, Short.MIN_VALUE+2 to Short.MAX_VALUE-1 => same as short value, Short.MIN_VALUE+1 => * readInt(). - * <p/> - * This method is suitable for reading the bytes written by the <code>writeCompactInt</code> method of interface + * + * <p>This method is suitable for reading the bytes written by the <code>writeCompactInt</code> method of interface * <code>RandomDataOutput</code>. * * @return the 32-bit value read. @@ -398,8 +407,8 @@ public interface RandomDataInput extends ObjectInput, RandomAccess, BytesCommon /** * Reads two or six input bytes and returns an <code>int</code> value. Let <code>a</code> be the first short read * with readShort(). This mapped as follows; -1 => readUnsignedInt(), default => (a & 0xFFFF) - * <p/> - * This method is suitable for reading the bytes written by the <code>writeCompactUnsignedInt</code> method of + * + * <p>This method is suitable for reading the bytes written by the <code>writeCompactUnsignedInt</code> method of * interface <code>RandomDataOutput</code>. * * @return the unsigned 32-bit value read. @@ -409,7 +418,7 @@ public interface RandomDataInput extends ObjectInput, RandomAccess, BytesCommon /** * Reads eight input bytes and returns a <code>long</code> value. Let <code>a-h</code> be the first through eighth * bytes read on big endian machines, and the opposite on little endian machines. The value returned is: - * <p><pre> <code> + * <pre> <code> * (((long)(a & 0xff) << 56) | * ((long)(b & 0xff) << 48) | * ((long)(c & 0xff) << 40) | @@ -419,8 +428,8 @@ public interface RandomDataInput extends ObjectInput, RandomAccess, BytesCommon * ((long)(g & 0xff) << 8) | * ((long)(h & 0xff))) * </code></pre> - * <p/> - * This method is suitable for reading bytes written by the <code>writeLong</code> method of interface + * + * <p>This method is suitable for reading bytes written by the <code>writeLong</code> method of interface * <code>DataOutput</code>. * * @return the <code>long</code> value read. @@ -429,9 +438,17 @@ public interface RandomDataInput extends ObjectInput, RandomAccess, BytesCommon long readLong(); /** + * Same as readLong except the remaining() can be less than 8. + * + * @param offset base + * @return long. + */ + long readIncompleteLong(long offset); + + /** * Reads eight input bytes and returns a <code>long</code> value. Let <code>a-h</code> be the first through eighth * bytes read on big endian machines, and the opposite on little endian machines. The value returned is: - * <p><pre> <code> + * <pre> <code> * (((long)(a & 0xff) << 56) | * ((long)(b & 0xff) << 48) | * ((long)(c & 0xff) << 40) | @@ -441,8 +458,8 @@ public interface RandomDataInput extends ObjectInput, RandomAccess, BytesCommon * ((long)(g & 0xff) << 8) | * ((long)(h & 0xff))) * </code></pre> - * <p/> - * This method is suitable for reading bytes written by the <code>writeLong</code> method of interface + * + * <p>This method is suitable for reading bytes written by the <code>writeLong</code> method of interface * <code>RandomDataOutput</code>. * * @param offset of the long to read @@ -452,10 +469,10 @@ public interface RandomDataInput extends ObjectInput, RandomAccess, BytesCommon /** * This is the same readLong() except a dread barrier is performed first - * <p/> - * Reads eight input bytes and returns a <code>long</code> value. Let <code>a-h</code> be the first through eighth + * + * <p>Reads eight input bytes and returns a <code>long</code> value. Let <code>a-h</code> be the first through eighth * bytes read on big endian machines, and the opposite on little endian machines. The value returned is: - * <p><pre> <code> + * <pre> <code> * (((long)(a & 0xff) << 56) | * ((long)(b & 0xff) << 48) | * ((long)(c & 0xff) << 40) | @@ -465,8 +482,8 @@ public interface RandomDataInput extends ObjectInput, RandomAccess, BytesCommon * ((long)(g & 0xff) << 8) | * ((long)(h & 0xff))) * </code></pre> - * <p/> - * This method is suitable for reading bytes written by the <code>writeOrderedLong</code> or + * + * <p>This method is suitable for reading bytes written by the <code>writeOrderedLong</code> or * <code>writeVolatileLong</code> method of interface <code>RandomDataOutput</code>. * * @return the <code>long</code> value read. @@ -475,10 +492,10 @@ public interface RandomDataInput extends ObjectInput, RandomAccess, BytesCommon /** * This is the same readLong() except a dread barrier is performed first - * <p/> - * Reads eight input bytes and returns a <code>long</code> value. Let <code>a-h</code> be the first through eighth + * + * <p>Reads eight input bytes and returns a <code>long</code> value. Let <code>a-h</code> be the first through eighth * bytes read on big endian machines, and the opposite on little endian machines. The value returned is: - * <p><pre> <code> + * <pre> <code> * (((long)(a & 0xff) << 56) | * ((long)(b & 0xff) << 48) | * ((long)(c & 0xff) << 40) | @@ -488,8 +505,8 @@ public interface RandomDataInput extends ObjectInput, RandomAccess, BytesCommon * ((long)(g & 0xff) << 8) | * ((long)(h & 0xff))) * </code></pre> - * <p/> - * This method is suitable for reading bytes written by the <code>writeOrderedLong</code> or + * + * <p>This method is suitable for reading bytes written by the <code>writeOrderedLong</code> or * <code>writeVolatileLong</code> method of interface <code>RandomDataOutput</code>. * * @param offset of the long to read @@ -500,7 +517,7 @@ public interface RandomDataInput extends ObjectInput, RandomAccess, BytesCommon /** * Reads six input bytes and returns a <code>long</code> value. Let <code>a-f</code> be the first through sixth * bytes read on big endian machines, and the opposite on little endian machines. The value returned is: - * <p><pre> <code> + * <pre> <code> * (((long)(a & 0xff) << 56) | * ((long)(b & 0xff) << 48) | * ((long)(c & 0xff) << 40) | @@ -508,8 +525,8 @@ public interface RandomDataInput extends ObjectInput, RandomAccess, BytesCommon * ((long)(e & 0xff) << 24) | * ((long)(f & 0xff) << 16)) >> 16 * </code></pre> - * <p/> - * This method is suitable for reading bytes written by the <code>writeInt48</code> method of interface + * + * <p>This method is suitable for reading bytes written by the <code>writeInt48</code> method of interface * <code>RandomDataOutput</code>. * * @return the <code>long</code> value read. @@ -519,7 +536,7 @@ public interface RandomDataInput extends ObjectInput, RandomAccess, BytesCommon /** * Reads six input bytes and returns a <code>long</code> value. Let <code>a-f</code> be the first through sixth * bytes read on big endian machines, and the opposite on little endian machines. The value returned is: - * <p><pre> <code> + * <pre> <code> * (((long)(a & 0xff) << 56) | * ((long)(b & 0xff) << 48) | * ((long)(c & 0xff) << 40) | @@ -527,8 +544,8 @@ public interface RandomDataInput extends ObjectInput, RandomAccess, BytesCommon * ((long)(e & 0xff) << 24) | * ((long)(f & 0xff) << 16)) >> 16 * </code></pre> - * <p/> - * This method is suitable for reading bytes written by the <code>writeInt48</code> method of interface + * + * <p>This method is suitable for reading bytes written by the <code>writeInt48</code> method of interface * <code>RandomDataOutput</code>. * * @param offset of the long to read @@ -540,8 +557,8 @@ public interface RandomDataInput extends ObjectInput, RandomAccess, BytesCommon * Reads four or twelve input bytes and returns a <code>long</code> value. Let <code>a</code> be the first int read * with readInt(). This mapped as follows; Integer.MIN_VALUE => Long.MIN_VALUE, Integer.MAX_VALUE => Long.MAX_VALUE, * Integer.MIN_VALUE+2 to Integer.MAX_VALUE-1 => same as short value, Integer.MIN_VALUE+1 => readLong(). - * <p/> - * This method is suitable for reading the bytes written by the <code>writeCompactLong</code> method of interface + * + * <p>This method is suitable for reading the bytes written by the <code>writeCompactLong</code> method of interface * <code>RandomDataOutput</code>. * * @return the 64-bit value read. @@ -550,14 +567,14 @@ public interface RandomDataInput extends ObjectInput, RandomAccess, BytesCommon /** * Reads between one and ten bytes with are stop encoded with support for negative numbers - * <p><pre><code> + * <pre><code> * long l = 0, b; * int count = 0; * while ((b = readByte()) < 0) { * l |= (b & 0x7FL) << count; * count += 7; * } - * if (b == 0 && count > 0) + * if (b == 0 && count > 0) * return ~l; * return l | (b << count); * </code></pre> @@ -593,8 +610,8 @@ public interface RandomDataInput extends ObjectInput, RandomAccess, BytesCommon /** * This is the same as readFloat() except a read barrier is performed first. <p> Reads four input bytes and returns * a <code>float</code> value. - * <p/> - * This method is suitable for reading bytes written by the <code>writeOrderedFloat</code> method of interface <code>RandomDataOutput</code>. + * + * <p>This method is suitable for reading bytes written by the <code>writeOrderedFloat</code> method of interface <code>RandomDataOutput</code>. * * @param offset to read from * @return the <code>int</code> value read. @@ -635,8 +652,8 @@ public interface RandomDataInput extends ObjectInput, RandomAccess, BytesCommon /** * This is the same as readDouble() except a read barrier is performed first. <p> Reads four input bytes and returns * a <code>float</code> value. - * <p/> - * This method is suitable for reading bytes written by the <code>writeOrderedFloat</code> method of interface <code>RandomDataOutput</code>. + * + * <p>This method is suitable for reading bytes written by the <code>writeOrderedFloat</code> method of interface <code>RandomDataOutput</code>. * * @param offset to read from * @return the <code>int</code> value read. @@ -648,8 +665,8 @@ public interface RandomDataInput extends ObjectInput, RandomAccess, BytesCommon * into a character, until it encounters a line terminator or end of file; the characters read are then returned as * a <code>String</code>. Note that because this method processes bytes, it does not support input of the full * Unicode character set. - * <p/> - * If end of file is encountered before even one byte can be read, then <code>null</code> is returned. Otherwise, + * + * <p>If end of file is encountered before even one byte can be read, then <code>null</code> is returned. Otherwise, * each byte that is read is converted to type <code>char</code> by zero-extension. If the character * <code>'\n'</code> is encountered, it is discarded and reading ceases. If the character <code>'\r'</code> is * encountered, it is discarded and, if the following byte converts  to the character <code>'\n'</code>, then @@ -660,7 +677,7 @@ public interface RandomDataInput extends ObjectInput, RandomAccess, BytesCommon * <code>(char)256</code>. * * @return the next line of text from the input stream, or <CODE>null</CODE> if the end of file is encountered - * before a byte can be read. + * before a byte can be read. */ @Override @Nullable @@ -670,43 +687,43 @@ public interface RandomDataInput extends ObjectInput, RandomAccess, BytesCommon * Reads in a string that has been encoded using a <a href="#modified-utf-8">modified UTF-8</a> format. The general * contract of <code>readUTF</code> is that it reads a representation of a Unicode character string encoded in * modified UTF-8 format; this string of characters is then returned as a <code>String</code>. - * <p/> - * First, two bytes are read and used to construct an unsigned 16-bit integer in exactly the manner of the + * + * <p>First, two bytes are read and used to construct an unsigned 16-bit integer in exactly the manner of the * <code>readUnsignedShort</code> method . This integer value is called the <i>UTF length</i> and specifies the * number of additional bytes to be read. These bytes are then converted to characters by considering them in * groups. The length of each group is computed from the value of the first byte of the group. The byte following a * group, if any, is the first byte of the next group. - * <p/> - * If the first byte of a group matches the bit pattern <code>0xxxxxxx</code> (where <code>x</code> means "may be + * + * <p>If the first byte of a group matches the bit pattern <code>0xxxxxxx</code> (where <code>x</code> means "may be * <code>0</code> or <code>1</code>"), then the group consists of just that byte. The byte is zero-extended to form * a character. - * <p/> - * If the first byte of a group matches the bit pattern <code>110xxxxx</code>, then the group consists of that byte + * + * <p>If the first byte of a group matches the bit pattern <code>110xxxxx</code>, then the group consists of that byte * <code>a</code> and a second byte <code>b</code>. If there is no byte <code>b</code> (because byte <code>a</code> * was the last of the bytes to be read), or if byte <code>b</code> does not match the bit pattern * <code>10xxxxxx</code>, then a <code>UTFDataFormatException</code> is thrown. Otherwise, the group is converted to - * the character:<p> + * the character: * <pre><code>(char)(((a& 0x1F) << 6) | (b & 0x3F)) * </code></pre> * If the first byte of a group matches the bit pattern <code>1110xxxx</code>, then the group consists of that byte * <code>a</code> and two more bytes <code>b</code> and <code>c</code>. If there is no byte <code>c</code> (because * byte <code>a</code> was one of the last two of the bytes to be read), or either byte <code>b</code> or byte * <code>c</code> does not match the bit pattern <code>10xxxxxx</code>, then a <code>UTFDataFormatException</code> - * is thrown. Otherwise, the group is converted to the character:<p> + * is thrown. Otherwise, the group is converted to the character: * <pre><code> * (char)(((a & 0x0F) << 12) | ((b & 0x3F) << 6) | (c & 0x3F)) * </code></pre> * If the first byte of a group matches the pattern <code>1111xxxx</code> or the pattern <code>10xxxxxx</code>, then * a <code>UTFDataFormatException</code> is thrown. - * <p/> - * If end of file is encountered at any time during this entire process, then an <code>EOFException</code> is + * + * <p>If end of file is encountered at any time during this entire process, then an <code>EOFException</code> is * thrown. - * <p/> - * After every group has been converted to a character by this process, the characters are gathered, in the same + * + * <p>After every group has been converted to a character by this process, the characters are gathered, in the same * order in which their corresponding groups were read from the input stream, to form a <code>String</code>, which * is returned. - * <p/> - * The <code>writeUTF</code> method of interface <code>DataOutput</code> may be used to write data that is suitable + * + * <p>The <code>writeUTF</code> method of interface <code>DataOutput</code> may be used to write data that is suitable * for reading by this method. * * @return a Unicode string. @@ -726,7 +743,7 @@ public interface RandomDataInput extends ObjectInput, RandomAccess, BytesCommon String readUTFΔ(); /** - * The same as readUTFΔ() except an offset and maximum length is given. + * The same as readUTFΔ() except an offset is given. * * @param offset to read from * @return a Unicode string or <code>null</code> if <code>writeUTFΔ(null)</code> was called @@ -738,10 +755,13 @@ public interface RandomDataInput extends ObjectInput, RandomAccess, BytesCommon /** * The same as readUTFΔ() except the chars are copied to a truncated StringBuilder. * + * @param stringBuilder to copy chars to * @return <code>true</code> if there was a String, or <code>false</code> if it was <code>null</code> */ boolean readUTFΔ(@NotNull StringBuilder stringBuilder); + boolean read8bitText(@NotNull StringBuilder stringBuilder) throws StreamCorruptedException; + /** * Copy bytes into a ByteBuffer to the minimum of the length <code>remaining()</code> in the ByteBuffer or the * Excerpt. @@ -751,12 +771,22 @@ public interface RandomDataInput extends ObjectInput, RandomAccess, BytesCommon void read(@NotNull ByteBuffer bb); /** + * Copy bytes into a ByteBuffer to the minimum of the length in the ByteBuffer or the + * Excerpt. + * + * @param bb to copy into + * @param length number of bytes to copy + */ + void read(@NotNull ByteBuffer bb, int length); + + /** * Read a String with <code>readUTFΔ</code> which is converted to an enumerable type. i.e. where there is a one to * one mapping between an object and it's toString(). - * <p/> - * This is suitable to read an object written using <code>writeEnum()</code> in the <code>RandomDataOutput</code> + * + * <p>This is suitable to read an object written using <code>writeEnum()</code> in the <code>RandomDataOutput</code> * interface * + * @param <E> the enum class * @param eClass to decode the String as * @return the decoded value. <code>null</code> with be return if null was written. */ @@ -764,21 +794,27 @@ public interface RandomDataInput extends ObjectInput, RandomAccess, BytesCommon <E> E readEnum(@NotNull Class<E> eClass); /** - * Read a stop bit encoded length and populates this Collection after clear()ing it. - * <p/> - * This is suitable to reading a list written using <code>writeList()</code> in the <code>RandomDataOutput</code> + * Read a stop bit encoded length and populates this Collection after zeroOut()ing it. + * + * <p>This is suitable to reading a list written using <code>writeList()</code> in the <code>RandomDataOutput</code> * interface * + * @param <E> the list element class + * @param eClass the list element class * @param list to populate */ <E> void readList(@NotNull Collection<E> list, @NotNull Class<E> eClass); /** - * Read a stop bit encoded length and populates this Map after clear()ing it. - * <p/> - * This is suitable to reading a list written using <code>writeMap()</code> in the <code>RandomDataOutput</code> + * Read a stop bit encoded length and populates this Map after zeroOut()ing it. + * + * <p>This is suitable to reading a list written using <code>writeMap()</code> in the <code>RandomDataOutput</code> * interface * + * @param <K> the map key class + * @param <V> the map value class + * @param kClass the map key class + * @param vClass the map value class * @param map to populate * @return the map */ @@ -800,6 +836,8 @@ public interface RandomDataInput extends ObjectInput, RandomAccess, BytesCommon /** * Read and return an object. The class that implements this interface defines where the object is "read" from. * + * @param <T> the class of the object to read + * @param tClass the class of the object to read * @return the object read from the stream * @throws IllegalStateException the class of a serialized object cannot be found or any of the usual Input/Output * related exceptions occur. @@ -809,7 +847,18 @@ public interface RandomDataInput extends ObjectInput, RandomAccess, BytesCommon <T> T readObject(Class<T> tClass) throws IllegalStateException; /** - * Reads a byte of data. This method will block if no input is available. + * Read an instance of a class assuming objClass was provided when written. + * + * @param <T> the class of the object to read + * @param objClass class to write + * @param obj to reuse or null if a new object is needed + * @return the object read from the stream + */ + @Nullable + <T> T readInstance(@NotNull Class<T> objClass, T obj); + + /** + * Reads a byte of data. This method is non blocking. * * @return the byte read, or -1 if the end of the stream is reached. */ @@ -865,4 +914,18 @@ public interface RandomDataInput extends ObjectInput, RandomAccess, BytesCommon */ @Override void close(); + + boolean startsWith(RandomDataInput input); + + boolean compare(long offset, RandomDataInput input, long inputOffset, long len); + + <E> E readEnum(long offset, int maxSize, Class<E> eClass); + + /** + * From a given bit index, find the next bit with is set. + * + * @param fromIndex first bit to scan. + * @return first bit equals or after it which is set. + */ + long nextSetBit(long fromIndex); } diff --git a/lang/src/main/java/net/openhft/lang/io/RandomDataOutput.java b/lang/src/main/java/net/openhft/lang/io/RandomDataOutput.java index 92a1a4c..d10f430 100755 --- a/lang/src/main/java/net/openhft/lang/io/RandomDataOutput.java +++ b/lang/src/main/java/net/openhft/lang/io/RandomDataOutput.java @@ -1,21 +1,22 @@ /* - * Copyright 2013 Peter Lawrey + * Copyright (C) 2015 higherfrequencytrading.com * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as published by + * the Free Software Foundation, either version 3 of the License. * - * http://www.apache.org/licenses/LICENSE-2.0 + * 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 Lesser General Public License for more details. * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. + * You should have received a copy of the GNU Lesser General Public License + * along with this program. If not, see <http://www.gnu.org/licenses/>. */ package net.openhft.lang.io; +import net.openhft.lang.model.Byteable; import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.Nullable; @@ -29,14 +30,50 @@ import java.util.RandomAccess; * @author peter.lawrey */ public interface RandomDataOutput extends ObjectOutput, RandomAccess, BytesCommon { + + /** + * Copies the contents of a RandomDataInput from the position to the limit. + * + * <p> This method transfers the bytes remaining in the given source + * buffer into this buffer. If there are more bytes remaining in the + * source buffer than in this buffer, that is, if + * <tt>src.remaining()</tt> <tt>></tt> <tt>remaining()</tt>, + * then no bytes are transferred and a {@link + * java.nio.BufferOverflowException} is thrown. + * + * <p> Otherwise, this method copies + * <i>n</i> = <tt>src.remaining()</tt> bytes from the given + * buffer into this buffer, starting at each buffer's current position. + * The positions of both buffers are then incremented by <i>n</i>. + * + * <p> In other words, an invocation of this method of the form + * <tt>dst.write(src)</tt> has exactly the same effect as the loop + * + * <pre> + * while (src.hasRemaining()) + * dst.writeByte(src.readByte()); + * </pre> + + * @param bytes to copy + */ + void write(RandomDataInput bytes); + + /** + * Copy from one Bytes to another, moves the position of "this" RandomDataOutput by the length. + * The position of the RandomDataInput is not used or altered. + * + * @param bytes to copy + * @param position to copy from + * @param length to copy + */ + void write(RandomDataInput bytes, long position, long length); + /** - * Copy from one Bytes to another. Copied from the start to the current position. + * Copies the contents of a Byteable from the offset for maxSize bytes, moves the position of "this" RandomDataOutput by the maxSize * - * @param bytes to copy from - * @deprecated Use write(BytesCommon bytes, long position, long length) instead. + * @param byteable to copy */ - @Deprecated - void writeStartToPosition(Bytes bytes); + void write(@NotNull Byteable byteable); /** * Writes to the output stream the eight low-order bits of the argument <code>b</code>. The 24 high-order bits of @@ -113,6 +150,8 @@ public interface RandomDataOutput extends ObjectOutput, RandomAccess, BytesCommo */ void write(long offset, byte[] bytes); + void write(long offset, Bytes bytes); + /** * Writes <code>len</code> bytes from array <code>bytes</code>, in order, to the output stream. If * <code>bytes</code> is <code>null</code>, a <code>NullPointerException</code> is thrown. If <code>off</code> is @@ -128,6 +167,12 @@ public interface RandomDataOutput extends ObjectOutput, RandomAccess, BytesCommo @Override void write(byte[] bytes, int off, int len); + void write(long offset, byte[] bytes, int off, int len); + + void write(@NotNull char[] data); + + void write(@NotNull char[] data, int off, int len); + /** * Writes a <code>boolean</code> value to this output stream. If the argument <code>v</code> is <code>true</code>, * the value <code>(byte)1</code> is written; if <code>v</code> is <code>false</code>, the value @@ -150,12 +195,11 @@ public interface RandomDataOutput extends ObjectOutput, RandomAccess, BytesCommo * @param offset to write boolean * @param v the boolean to be written. */ - void writeBoolean(long offset, boolean v); /** * Writes two bytes to the output stream to represent the value of the argument. The byte values to be written, in - * the order shown for big endian machines and the opposite for little endian, are: <p> + * the order shown for big endian machines and the opposite for little endian, are: * <pre><code> * (byte)(0xff & (v >> 8)) * (byte)(0xff & v) @@ -170,7 +214,7 @@ public interface RandomDataOutput extends ObjectOutput, RandomAccess, BytesCommo /** * Writes two bytes to the output stream to represent the value of the argument. The byte values to be written, in - * the order shown for big endian machines and the opposite for little endian, are: <p> + * the order shown for big endian machines and the opposite for little endian, are: * <pre><code> * (byte)(0xff & (v >> 8)) * (byte)(0xff & v) @@ -185,7 +229,7 @@ public interface RandomDataOutput extends ObjectOutput, RandomAccess, BytesCommo /** * Writes two bytes to the output stream to represent the value of the argument. The byte values to be written, in - * the order shown for big endian machines and the opposite for little endian, are: <p> + * the order shown for big endian machines and the opposite for little endian, are: * <pre><code> * (byte)(0xff & (v >> 8)) * (byte)(0xff & v) @@ -199,7 +243,7 @@ public interface RandomDataOutput extends ObjectOutput, RandomAccess, BytesCommo /** * Writes two bytes to the output stream to represent the value of the argument. The byte values to be written, in - * the order shown for big endian machines and the opposite for little endian, are: <p> + * the order shown for big endian machines and the opposite for little endian, are: * <pre><code> * (byte)(0xff & (v >> 8)) * (byte)(0xff & v) @@ -216,8 +260,8 @@ public interface RandomDataOutput extends ObjectOutput, RandomAccess, BytesCommo * Writes one or three bytes as follows; Short.MIN_VALUE => Byte.MIN_VALUE, Short.MAX_VALUE => Byte.MAX_VALUE, * Short.MIN_VALUE+2 to Short.MAX_VALUE-1 => writeByte(x), default => writeByte(Byte.MIN_VALUE+1; * writeShort(x) - * <p/> - * The bytes written by this method may be read by the <code>readCompactShort</code> method of interface + * + * <p>The bytes written by this method may be read by the <code>readCompactShort</code> method of interface * <code>RandomDataInput</code> , which will then return a <code>short</code> equal to <code>(short)v</code>. * * @param v the <code>short</code> value to be written. @@ -226,8 +270,8 @@ public interface RandomDataOutput extends ObjectOutput, RandomAccess, BytesCommo /** * Writes one or three bytes as follows; 0 to 254 => writeByte(x); otherwise writeByte(255); writeByteShort(x); - * <p/> - * The bytes written by this method may be read by the <code>readCompactUnsignedShort</code> method of interface + * + * <p>The bytes written by this method may be read by the <code>readCompactUnsignedShort</code> method of interface * <code>RandomDataInput</code> , which will then return a <code>short</code> equal to <code>v & 0xFFFF</code>. * * @param v the unsigned <code>short</code> value to be written. @@ -237,7 +281,7 @@ public interface RandomDataOutput extends ObjectOutput, RandomAccess, BytesCommo /** * Writes a <code>char</code> value, which is comprised of two bytes, to the output stream. The byte values to be * written, in the order shown for big endian machines and the opposite for little endian, are: - * <p><pre><code> + * <pre><code> * (byte)(0xff & (v >> 8)) * (byte)(0xff & v) * </code></pre><p> @@ -252,7 +296,7 @@ public interface RandomDataOutput extends ObjectOutput, RandomAccess, BytesCommo /** * Writes a <code>char</code> value, which is comprised of two bytes, to the output stream. The byte values to be * written, in the order shown for big endian machines and the opposite for little endian, are: - * <p><pre><code> + * <pre><code> * (byte)(0xff & (v >> 8)) * (byte)(0xff & v) * </code></pre><p> @@ -267,7 +311,7 @@ public interface RandomDataOutput extends ObjectOutput, RandomAccess, BytesCommo /** * Writes an <code>int</code> value, which is comprised of three bytes, to the output stream. The byte values to be * written, in the order shown for big endian machines and the opposite for little endian, are: - * <p><pre><code> + * <pre><code> * (byte)(0xff & (v >> 16)) * (byte)(0xff & (v >>    8)) * (byte)(0xff & v) @@ -282,7 +326,7 @@ public interface RandomDataOutput extends ObjectOutput, RandomAccess, BytesCommo /** * Writes an <code>int</code> value, which is comprised of three bytes, to the output stream. The byte values to be * written, in the order shown for big endian machines and the opposite for little endian, are: - * <p><pre><code> + * <pre><code> * (byte)(0xff & (v >> 16)) * (byte)(0xff & (v >>    8)) * (byte)(0xff & v) @@ -298,7 +342,7 @@ public interface RandomDataOutput extends ObjectOutput, RandomAccess, BytesCommo /** * Writes an <code>int</code> value, which is comprised of four bytes, to the output stream. The byte values to be * written, in the order shown for big endian machines and the opposite for little endian, are: - * <p><pre><code> + * <pre><code> * (byte)(0xff & (v >> 24)) * (byte)(0xff & (v >> 16)) * (byte)(0xff & (v >>    8)) @@ -315,7 +359,7 @@ public interface RandomDataOutput extends ObjectOutput, RandomAccess, BytesCommo /** * Writes an <code>int</code> value, which is comprised of four bytes, to the output stream. The byte values to be * written, in the order shown for big endian machines and the opposite for little endian, are: - * <p><pre><code> + * <pre><code> * (byte)(0xff & (v >> 24)) * (byte)(0xff & (v >> 16)) * (byte)(0xff & (v >>    8)) @@ -332,7 +376,7 @@ public interface RandomDataOutput extends ObjectOutput, RandomAccess, BytesCommo /** * Writes an <code>int</code> value, which is comprised of four bytes, to the output stream. The byte values to be * written, in the order shown for big endian machines and the opposite for little endian, are: - * <p><pre><code> + * <pre><code> * (byte)(0xff & (v >> 24)) * (byte)(0xff & (v >> 16)) * (byte)(0xff & (v >>    8)) @@ -349,7 +393,7 @@ public interface RandomDataOutput extends ObjectOutput, RandomAccess, BytesCommo /** * Writes an <code>int</code> value, which is comprised of four bytes, to the output stream. The byte values to be * written, in the order shown for big endian machines and the opposite for little endian, are: - * <p><pre><code> + * <pre><code> * (byte)(0xff & (v >> 24)) * (byte)(0xff & (v >> 16)) * (byte)(0xff & (v >>    8)) @@ -368,8 +412,8 @@ public interface RandomDataOutput extends ObjectOutput, RandomAccess, BytesCommo * Writes two or six bytes as follows; Integer.MIN_VALUE => Short.MIN_VALUE, Integer.MAX_VALUE => * Short.MAX_VALUE, Short.MIN_VALUE+2 to Short.MAX_VALUE-1 => writeShort(x), default => * writeShort(Short.MIN_VALUE+1; writeInt(x) - * <p/> - * The bytes written by this method may be read by the <code>readCompactInt</code> method of interface + * + * <p>The bytes written by this method may be read by the <code>readCompactInt</code> method of interface * <code>RandomDataInput</code> , which will then return a <code>int</code> equal to <code>v</code>. * * @param v the <code>int</code> value to be written. @@ -379,8 +423,8 @@ public interface RandomDataOutput extends ObjectOutput, RandomAccess, BytesCommo /** * Writes two or six bytes as follows; 0 to (1 << 16) - 2 => writeInt(x), otherwise writeShort(-1); * writeInt(x) - * <p/> - * The bytes written by this method may be read by the <code>readCompactUnsignedInt</code> method of interface + * + * <p>The bytes written by this method may be read by the <code>readCompactUnsignedInt</code> method of interface * <code>RandomDataInput</code> , which will then return a <code>int</code> equal to <code>v & * 0xFFFFFFFF</code>. * @@ -392,8 +436,8 @@ public interface RandomDataOutput extends ObjectOutput, RandomAccess, BytesCommo * Same as writeInt but include an ordered write barrier. This means all writes will be visible on a read barrier * if this write is visible. This might not be visible to be same thread for some clock cycles so an immediate read * could see an old value - * <p/> - * This is much faster than a volatile write which stalls the pipeline. The data is visible to other threads at the + * + * <p>This is much faster than a volatile write which stalls the pipeline. The data is visible to other threads at the * same time. * * @param v value to write @@ -404,8 +448,8 @@ public interface RandomDataOutput extends ObjectOutput, RandomAccess, BytesCommo * Same as writeInt but include an ordered write barrier. This means all writes will be visible on a read barrier * if this write is visible. This might not be visible to be same thread for some clock cycles so an immediate read * could see an old value - * <p/> - * This is much faster than <code>writeVolatileInt</code> as the volatile write stalls the pipeline. The data is + * + * <p>This is much faster than <code>writeVolatileInt</code> as the volatile write stalls the pipeline. The data is * visible to other threads at the same time. * * @param offset to write to @@ -445,7 +489,7 @@ public interface RandomDataOutput extends ObjectOutput, RandomAccess, BytesCommo /** * Writes a <code>long</code> value, which is comprised of eight bytes, to the output stream. The byte values to be * written, in the order shown for big endian machines and the opposite for little endian, are: - * <p><pre><code> + * <pre><code> * (byte)(0xff & (v >> 40)) * (byte)(0xff & (v >> 32)) * (byte)(0xff & (v >> 24)) @@ -454,7 +498,7 @@ public interface RandomDataOutput extends ObjectOutput, RandomAccess, BytesCommo * (byte)(0xff & v) * </code></pre><p> * The bytes written by this method may be read by the <code>readInt48</code> method of interface - * <code>RandomDataInput</code> , which will then return a <code>long</code> equal to <code>v & ((1L << 48) + * <code>RandomDataInput</code> , which will then return a <code>long</code> equal to <code>v & ((1L << 48) * - 1)</code>. * * @param v the <code>long</code> value to be written. @@ -464,7 +508,7 @@ public interface RandomDataOutput extends ObjectOutput, RandomAccess, BytesCommo /** * Writes a <code>long</code> value, which is comprised of eight bytes, to the output stream. The byte values to be * written, in the order shown for big endian machines and the opposite for little endian, are: - * <p><pre><code> + * <pre><code> * (byte)(0xff & (v >> 40)) * (byte)(0xff & (v >> 32)) * (byte)(0xff & (v >> 24)) @@ -473,7 +517,7 @@ public interface RandomDataOutput extends ObjectOutput, RandomAccess, BytesCommo * (byte)(0xff & v) * </code></pre><p> * The bytes written by this method may be read by the <code>readInt48</code> method of interface - * <code>RandomDataInput</code> , which will then return a <code>long</code> equal to <code>v & ((1L << 48) + * <code>RandomDataInput</code> , which will then return a <code>long</code> equal to <code>v & ((1L << 48) * - 1)</code>. * * @param offset to be written to @@ -484,7 +528,7 @@ public interface RandomDataOutput extends ObjectOutput, RandomAccess, BytesCommo /** * Writes a <code>long</code> value, which is comprised of eight bytes, to the output stream. The byte values to be * written, in the order shown for big endian machines and the opposite for little endian, are: - * <p><pre><code> + * <pre><code> * (byte)(0xff & (v >> 56)) * (byte)(0xff & (v >> 48)) * (byte)(0xff & (v >> 40)) @@ -505,7 +549,7 @@ public interface RandomDataOutput extends ObjectOutput, RandomAccess, BytesCommo /** * Writes a <code>long</code> value, which is comprised of eight bytes, to the output stream. The byte values to be * written, in the order shown for big endian machines and the opposite for little endian, are: - * <p><pre><code> + * <pre><code> * (byte)(0xff & (v >> 56)) * (byte)(0xff & (v >> 48)) * (byte)(0xff & (v >> 40)) @@ -527,8 +571,8 @@ public interface RandomDataOutput extends ObjectOutput, RandomAccess, BytesCommo * Writes four or twelve bytes as follows Long.MIN_VALUE => Integer.MIN_VALUE, Long.MAX_VALUE => * Integer.MAX_VALUE, Integer.MIN_VALUE+2 to Integer.MAX_VALUE-1 => writeInt(x), default => * writeInt(Integer.MIN_VALUE+1; writeLong(x) - * <p/> - * The bytes written by this method may be read by the <code>readCompactLong</code> method of interface + * + * <p>The bytes written by this method may be read by the <code>readCompactLong</code> method of interface * <code>RandomDataInput</code> , which will then return a <code>long</code> equal to <code>v</code>. * * @param v the <code>long</code> value to be written. @@ -539,8 +583,8 @@ public interface RandomDataOutput extends ObjectOutput, RandomAccess, BytesCommo * Same as writeLong but include an ordered write barrier. This means all writes will be visible on a read barrier * if this write is visible. This might not be visible to be same thread for some clock cycles so an immediate read * could see an old value - * <p/> - * This is much faster than a volatile write which stalls the pipeline. The data is visible to other threads at the + * + * <p>This is much faster than a volatile write which stalls the pipeline. The data is visible to other threads at the * same time. * * @param v value to write @@ -551,8 +595,8 @@ public interface RandomDataOutput extends ObjectOutput, RandomAccess, BytesCommo * Same as writeLong but include an ordered write barrier. This means all writes will be visible on a read barrier * if this write is visible. This might not be visible to be same thread for some clock cycles so an immediate read * could see an old value - * <p/> - * This is much faster than a volatile write which stalls the pipeline. The data is visible to other threads at the + * + * <p>This is much faster than a volatile write which stalls the pipeline. The data is visible to other threads at the * same time. * * @param offset to be written to @@ -572,7 +616,21 @@ public interface RandomDataOutput extends ObjectOutput, RandomAccess, BytesCommo boolean compareAndSwapLong(long offset, long expected, long x); /** - * Stop bit encoding numbers. This will write the same number of bytes whether you used a byte, short or int. + * Perform a compare and set operation. The value is set to <code>x</code> provided the <code>expected</code> value + * is set already. This operation is atomic. + * + * @param offset to write to. + * @param expected to expect + * @param x to set if expected was found + * @return true if set, false if the value was not expected + */ + boolean compareAndSwapDouble(long offset, double expected, double x); + + /** + * Stop bit encoding numbers. This will write the same number of bytes + * whether you used a byte, short or int. + * + * @param n the number to write */ void writeStopBit(long n); @@ -604,11 +662,12 @@ public interface RandomDataOutput extends ObjectOutput, RandomAccess, BytesCommo * Same as writeFloat but include an ordered write barrier. This means all writes will be visible on a read barrier * if this write is visible. This might not be visible to be same thread for some clock cycles so an immediate read * could see an old value - * <p/> - * This is much faster than a volatile write which stalls the pipeline. The data is visible to other threads at the + * + * <p>This is much faster than a volatile write which stalls the pipeline. The data is visible to other threads at the * same time. * - * @param v value to write + * @param offset to write to + * @param v value to write */ void writeOrderedFloat(long offset, float v); @@ -639,15 +698,16 @@ public interface RandomDataOutput extends ObjectOutput, RandomAccess, BytesCommo void writeDouble(long offset, double v); /** + * <p> * Writes four or twelve bytes as follow; - * <p><pre><code> + * </p><pre><code> * if ((float) d == d) { * writeFloat((float) d); * } else { * writeFloat(Float.NaN); * writeDouble(d); * } - * <p/> + * </code></pre> * The bytes written by this method may be read by the <code>readCompactDouble</code> method of interface * <code>RandomDataInput</code> , which will then return a <code>double</code> equal to <code>v</code>. * @@ -659,11 +719,12 @@ public interface RandomDataOutput extends ObjectOutput, RandomAccess, BytesCommo * Same as writeDouble but include an ordered write barrier. This means all writes will be visible on a read * barrier if this write is visible. This might not be visible to be same thread for some clock cycles so an * immediate read could see an old value - * <p/> - * This is much faster than a volatile write which stalls the pipeline. The data is visible to other threads at the + * + * <p>This is much faster than a volatile write which stalls the pipeline. The data is visible to other threads at the * same time. * - * @param v value to write + * @param offset to write to + * @param v value to write */ void writeOrderedDouble(long offset, double v); @@ -688,27 +749,36 @@ public interface RandomDataOutput extends ObjectOutput, RandomAccess, BytesCommo * bytes are actually written, high-order byte first, in exactly the manner of the <code>writeChar</code> method. * * @param s the string value to be written. Cannot be null. + * @see #writeChars(CharSequence) */ @Override void writeChars(@NotNull String s); /** + * Writes chars of the given {@code CharSequence} to the bytes, without encoding. + * + * @param cs the {@code CharSequence} to be written. Cannot be null. + * @see #writeChars(String) + */ + void writeChars(@NotNull CharSequence cs); + + /** * Writes two bytes of length information to the output stream, followed by the <a * href="DataInput.html#modified-utf-8">modified UTF-8</a> representation of every character in the string * <code>s</code>. If <code>s</code> is <code>null</code>, a <code>NullPointerException</code> is thrown. Each * character in the string <code>s</code> is converted to a group of one, two, or three bytes, depending on the * value of the character.<p> If a character <code>c</code> is in the range <code>\u0001</code> through - * <code>\u007f</code>, it is represented by one byte:<p> + * <code>\u007f</code>, it is represented by one byte: * <pre>(byte)c </pre> <p> * If a character <code>c</code> is <code>\u0000</code> or is in the range <code>\u0080</code> through * <code>\u07ff</code>, then it is represented by two bytes, to be written - * in the order shown:<p> <pre><code> + * in the order shown:<pre><code> * (byte)(0xc0 | (0x1f & (c >> 6))) * (byte)(0x80 | (0x3f & c)) * </code></pre> <p> If a character * <code>c</code> is in the range <code>\u0800</code> through <code>uffff</code>, then it is represented by * three bytes, to be written - * in the order shown:<p> <pre><code> + * in the order shown:<pre><code> * (byte)(0xe0 | (0x0f & (c >> 12))) * (byte)(0x80 | (0x3f & (c >> 6))) * (byte)(0x80 | (0x3f & c)) @@ -730,9 +800,9 @@ public interface RandomDataOutput extends ObjectOutput, RandomAccess, BytesCommo * i.e. one byte longer for short strings, but is not limited in length. 2) The string can be null. * * @param s the string value to be written. Can be null. + * @throws IllegalArgumentException if there is not enough space left */ - void writeUTFΔ(@Nullable CharSequence s); - + void writeUTFΔ(@Nullable CharSequence s) throws IllegalArgumentException; /** * Write the same encoding as <code>writeUTF</code> with the following changes. 1) The length is stop bit encoded @@ -745,8 +815,29 @@ public interface RandomDataOutput extends ObjectOutput, RandomAccess, BytesCommo */ void writeUTFΔ(long offset, int maxSize, @Nullable CharSequence s) throws IllegalStateException; + void write8bitText(@Nullable CharSequence s); /** - * Copies the contents of a ByteBuffer from the potision ot the limit. + * Copies the contents of a ByteBuffer from the position to the limit. + * + * <p> This method transfers the bytes remaining in the given source + * buffer into this buffer. If there are more bytes remaining in the + * source buffer than in this buffer, that is, if + * <tt>src.remaining()</tt> <tt>></tt> <tt>remaining()</tt>, + * then no bytes are transferred and a {@link + * java.nio.BufferOverflowException} is thrown. + * + * <p> Otherwise, this method copies + * <i>n</i> = <tt>src.remaining()</tt> bytes from the given + * buffer into this buffer, starting at each buffer's current position. + * The positions of both buffers are then incremented by <i>n</i>. + * + * <p> In other words, an invocation of this method of the form + * <tt>dst.write(src)</tt> has exactly the same effect as the loop + * + * <pre> + * while (src.hasRemaining()) + * dst.writeByte(src.get()); + * </pre> * * @param bb to copy. */ @@ -766,10 +857,11 @@ public interface RandomDataOutput extends ObjectOutput, RandomAccess, BytesCommo /** * Write an ordered collection of "enumerable objects" (See writeEnum). This writes the stop bit encoded length, * followed by multiple calls to <code>writeEnum</code> All the elements must be of the same type. - * <p/> - * This can be read by the <code>readList</code> method of <code>RandomInputStream</code> and the reader must know + * + * <p>This can be read by the <code>readList</code> method of <code>RandomInputStream</code> and the reader must know * the type of each element. You can send the class first by using <code>writeEnum</code> of the element class * + * @param <E> the class of the list elements * @param list to be written */ <E> void writeList(@NotNull Collection<E> list); @@ -779,9 +871,10 @@ public interface RandomDataOutput extends ObjectOutput, RandomAccess, BytesCommo * length, followed by multiple calls to <code>writeEnum</code> for each key and value. All the keys must be of the * same type. All values must be of the same type. * + * @param <K> the class of the map keys + * @param <V> the class of the map values * @param map to write out */ - <K, V> void writeMap(@NotNull Map<K, V> map); // ObjectOutput @@ -797,6 +890,15 @@ public interface RandomDataOutput extends ObjectOutput, RandomAccess, BytesCommo void writeObject(@Nullable Object object); /** + * Write an object with the assumption that the objClass will be provided when the class is read. + * + * @param <OBJ> the class of the object to write + * @param objClass class to write + * @param obj to write + */ + <OBJ> void writeInstance(@NotNull Class<OBJ> objClass, @NotNull OBJ obj); + + /** * Copy data from an Object from bytes start to end. * * @param object to copy from @@ -806,6 +908,32 @@ public interface RandomDataOutput extends ObjectOutput, RandomAccess, BytesCommo void writeObject(Object object, int start, int end); /** + * fill the Bytes with zeros, and clear the position. + * + * @return this + */ + Bytes zeroOut(); + + /** + * fill the Bytes with zeros. + * + * @param start first byte to zero out + * @param end the first byte after the last to zero out (exclusive bound) + * @return this + */ + Bytes zeroOut(long start, long end); + + /** + * fill the Bytes with zeros, and clear the position, avoiding touching pages unnecessarily + * + * @param start first byte to zero out + * @param end the first byte after the last to zero out (exclusive bound) + * @param ifNotZero only set to zero after checking the value is not zero. + * @return this + */ + Bytes zeroOut(long start, long end, boolean ifNotZero); + + /** * Check the end of the stream has not overflowed. Otherwise this doesn't do anything. */ @Override @@ -816,4 +944,6 @@ public interface RandomDataOutput extends ObjectOutput, RandomAccess, BytesCommo */ @Override void close(); + + void writeEnum(long offset, int len, Object object); } diff --git a/lang/src/main/java/net/openhft/lang/io/RandomDataUpdate.java b/lang/src/main/java/net/openhft/lang/io/RandomDataUpdate.java index c019276..686cd69 100644 --- a/lang/src/main/java/net/openhft/lang/io/RandomDataUpdate.java +++ b/lang/src/main/java/net/openhft/lang/io/RandomDataUpdate.java @@ -1,17 +1,17 @@ /* - * Copyright 2013 Peter Lawrey + * Copyright (C) 2015 higherfrequencytrading.com * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as published by + * the Free Software Foundation, either version 3 of the License. * - * http://www.apache.org/licenses/LICENSE-2.0 + * 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 Lesser General Public License for more details. * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. + * You should have received a copy of the GNU Lesser General Public License + * along with this program. If not, see <http://www.gnu.org/licenses/>. */ package net.openhft.lang.io; @@ -90,9 +90,25 @@ public interface RandomDataUpdate { void unlockInt(long offset) throws IllegalMonitorStateException; /** + * Lock which uses 4 bytes. Reset forces the lock to be cleared. Use this only when the program believes the + * locked thread is dead. + * + * @param offset of the start of the 4-byte lock + */ + void resetLockInt(long offset); + + /** + * Lock which uses 4 bytes. This returns the lower bytes which contain the threadId. + * + * @param offset of the start of the 4-byte lock + * @return the threadId or 0 if no thread holds the lock. + */ + int threadIdForLockInt(long offset); + + /** * Lock across processes - * <p/> - * Lock which uses 8 bytes. It store the lower 32 bits of the Thread Id, 16 bits are the process id and the re-entrant count as 16 bit. This + * + * <p>Lock which uses 8 bytes. It store the lower 32 bits of the Thread Id, 16 bits are the process id and the re-entrant count as 16 bit. This * means if you create more than 16 million threads you can get a collision, and if you try to re-enter 65535 times * you will get an ISE * @@ -103,8 +119,8 @@ public interface RandomDataUpdate { /** * Lock across processes - * <p/> - * Lock which uses 8 bytes. It store the lower 32 bits of the Thread Id, 16 bits are the process id and the re-entrant count as 16 bit. This + * + * <p>Lock which uses 8 bytes. It store the lower 32 bits of the Thread Id, 16 bits are the process id and the re-entrant count as 16 bit. This * means if you create more than 16 million threads you can get a collision, and if you try to re-enter 65535 times * you will get an ISE * @@ -116,8 +132,8 @@ public interface RandomDataUpdate { /** * Lock across processes - * <p/> - * Lock which uses 8 bytes. It store the lower 32 bits of the Thread Id, 16 bits are the process id and the re-entrant count as 16 bit. This + * + * <p>Lock which uses 8 bytes. It store the lower 32 bits of the Thread Id, 16 bits are the process id and the re-entrant count as 16 bit. This * means if you create more than 16 million threads you can get a collision, and if you try to re-enter 65535 times * you will get an ISE * @@ -129,8 +145,8 @@ public interface RandomDataUpdate { /** * Lock across processes - * <p/> - * Lock which uses 8 bytes. It store the lower 32 bits of the Thread Id, 16 bits are the process id and the re-entrant count as 16 bit. This + * + * <p>Lock which uses 8 bytes. It store the lower 32 bits of the Thread Id, 16 bits are the process id and the re-entrant count as 16 bit. This * means if you create more than 16 million threads you can get a collision, and if you try to re-enter 65535 times * you will get an ISE * @@ -138,4 +154,60 @@ public interface RandomDataUpdate { * @throws IllegalMonitorStateException if this thread doesn't hold the lock */ void unlockLong(long offset) throws IllegalMonitorStateException; + + /** + * Lock which uses 8 bytes. Reset forces the lock to be cleared. Use this only when the program believes the + * locked thread is dead. + * + * @param offset of the start of the 8-byte lock + */ + void resetLockLong(long offset); + + /** + * Lock which uses 8 bytes. This returns the lower bytes which contain the threadId. + * + * @param offset of the start of the 8-byte lock + * @return the threadId or 0 if no thread holds the lock. + */ + long threadIdForLockLong(long offset); + + /** + * Uses the 64-bit long at the offset as a non-reentrant read/write lock. + * There can be up to 2^20-1 threads reading, or waiting to read on a lock. + * + * @param offset of the long monitor + * @param timeOutNS length of time to busy wait for the lock. + * @return if the lock could be obtained in time. + * @throws java.lang.IllegalStateException if the monitor is in an illegal state + */ + boolean tryRWReadLock(long offset, long timeOutNS) throws IllegalStateException, InterruptedException; + + /** + * Uses the 64-bit long at the offset as a non-reentrant read/write lock. + * There can be up to 2^20-1 threads reading, or waiting to read on a lock. + * + * @param offset of the long monitor + * @param timeOutNS length of time to busy wait for the lock. + * @return if the lock could be obtained in time. + * @throws java.lang.IllegalStateException if the monitor is in an illegal state + */ + boolean tryRWWriteLock(long offset, long timeOutNS) throws IllegalStateException, InterruptedException; + + /** + * Uses the 64-bit long at the offset as a non-reentrant read/write lock. + * There can be up to 2^20-1 threads reading, or waiting to read on a lock. + * + * @param offset of the long monitor + * @throws java.lang.IllegalStateException if the monitor is in an illegal state + */ + void unlockRWReadLock(long offset) throws IllegalStateException; + + /** + * Uses the 64-bit long at the offset as a non-reentrant read/write lock. + * There can be up to 2^20-1 threads reading, or waiting to read on a lock. + * + * @param offset of the long monitor + * @throws java.lang.IllegalStateException if the monitor is in an illegal state + */ + void unlockRWWriteLock(long offset) throws IllegalStateException; } diff --git a/lang/src/main/java/net/openhft/lang/io/ResizeableMappedStore.java b/lang/src/main/java/net/openhft/lang/io/ResizeableMappedStore.java new file mode 100644 index 0000000..6580b51 --- /dev/null +++ b/lang/src/main/java/net/openhft/lang/io/ResizeableMappedStore.java @@ -0,0 +1,56 @@ +/* + * Copyright (C) 2015 higherfrequencytrading.com + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as published by + * the Free Software Foundation, either version 3 of the License. + * + * 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 Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with this program. If not, see <http://www.gnu.org/licenses/>. + */ +package net.openhft.lang.io; + +import net.openhft.lang.io.serialization.BytesMarshallableSerializer; +import net.openhft.lang.io.serialization.JDKZObjectSerializer; +import net.openhft.lang.io.serialization.ObjectSerializer; +import net.openhft.lang.io.serialization.impl.VanillaBytesMarshallerFactory; + +import java.io.File; +import java.io.IOException; +import java.nio.channels.FileChannel; + +public final class ResizeableMappedStore extends AbstractMappedStore { + public ResizeableMappedStore(File file, FileChannel.MapMode mode, long size) + throws IOException { + this(file, mode, size, BytesMarshallableSerializer.create( + new VanillaBytesMarshallerFactory(), JDKZObjectSerializer.INSTANCE)); + } + + public ResizeableMappedStore(File file, FileChannel.MapMode mode, long size, + ObjectSerializer objectSerializer) throws IOException { + super(new MmapInfoHolder(), file, mode, 0L, size, objectSerializer); + } + + /** + * Resizes the underlying file and re-maps it. Warning! After this call + * instances of {@link Bytes} obtained through {@link #bytes()} or + * {@link #bytes(long, long)} are invalid and using them can lead to reading + * arbitrary data or JVM crash! It is the callers responsibility to ensure + * that these instances are not used after the method call. + * + * @param newSize + * @throws IOException + */ + public void resize(long newSize) throws IOException { + validateSize(newSize); + unmapAndSyncToDisk(); + resizeIfNeeded(0L, newSize); + this.mmapInfoHolder.setSize(newSize); + map(0L); + } +} diff --git a/lang/src/main/java/net/openhft/lang/io/Reuses.java b/lang/src/main/java/net/openhft/lang/io/Reuses.java new file mode 100644 index 0000000..7740fb3 --- /dev/null +++ b/lang/src/main/java/net/openhft/lang/io/Reuses.java @@ -0,0 +1,34 @@ +/* + * Copyright (C) 2015 higherfrequencytrading.com + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as published by + * the Free Software Foundation, either version 3 of the License. + * + * 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 Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with this program. If not, see <http://www.gnu.org/licenses/>. + */ + +package net.openhft.lang.io; + +class Reuses { + static final ClassLoader MAGIC_CLASS_LOADER = findMagicClassLoader(); + + private static ClassLoader findMagicClassLoader() { + try { + Class<?> clazz = Class.forName("sun.reflect.ConstructorAccessor"); + ClassLoader cl = clazz.getClassLoader(); + if (cl == null) { + cl = ClassLoader.getSystemClassLoader(); + } + return cl; + } catch (Throwable ignore) { + } + return null; + } +} diff --git a/lang/src/main/java/net/openhft/lang/io/SettableAtt.java b/lang/src/main/java/net/openhft/lang/io/SettableAtt.java new file mode 100644 index 0000000..1adb5f4 --- /dev/null +++ b/lang/src/main/java/net/openhft/lang/io/SettableAtt.java @@ -0,0 +1,21 @@ +/* + * Copyright (C) 2015 higherfrequencytrading.com + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as published by + * the Free Software Foundation, either version 3 of the License. + * + * 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 Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with this program. If not, see <http://www.gnu.org/licenses/>. + */ + +package net.openhft.lang.io; + +final class SettableAtt { + Object att; +} diff --git a/lang/src/main/java/net/openhft/lang/io/StopCharTester.java b/lang/src/main/java/net/openhft/lang/io/StopCharTester.java index 8f965e3..52c5c21 100755..100644 --- a/lang/src/main/java/net/openhft/lang/io/StopCharTester.java +++ b/lang/src/main/java/net/openhft/lang/io/StopCharTester.java @@ -1,17 +1,17 @@ /* - * Copyright 2013 Peter Lawrey + * Copyright (C) 2015 higherfrequencytrading.com * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as published by + * the Free Software Foundation, either version 3 of the License. * - * http://www.apache.org/licenses/LICENSE-2.0 + * 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 Lesser General Public License for more details. * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. + * You should have received a copy of the GNU Lesser General Public License + * along with this program. If not, see <http://www.gnu.org/licenses/>. */ package net.openhft.lang.io; @@ -22,10 +22,10 @@ package net.openhft.lang.io; public interface StopCharTester { /** * Detect which byte stops the string to be parsed - * <p/> - * This should be changed to support char instead. - * <p/> - * Note: for safety reasons, you should stop on a 0 byte or throw an IllegalStateException. + * + * <p>This should be changed to support char instead. + * + * <p>Note: for safety reasons, you should stop on a 0 byte or throw an IllegalStateException. * * @param ch to test, 0 should return true or throw an exception. * @return if this byte is a stop character. diff --git a/lang/src/main/java/net/openhft/lang/io/StopCharTesters.java b/lang/src/main/java/net/openhft/lang/io/StopCharTesters.java index f711706..f463222 100755..100644 --- a/lang/src/main/java/net/openhft/lang/io/StopCharTesters.java +++ b/lang/src/main/java/net/openhft/lang/io/StopCharTesters.java @@ -1,22 +1,22 @@ /* - * Copyright 2013 Peter Lawrey + * Copyright (C) 2015 higherfrequencytrading.com * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as published by + * the Free Software Foundation, either version 3 of the License. * - * http://www.apache.org/licenses/LICENSE-2.0 + * 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 Lesser General Public License for more details. * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. + * You should have received a copy of the GNU Lesser General Public License + * along with this program. If not, see <http://www.gnu.org/licenses/>. */ package net.openhft.lang.io; -import org.jetbrains.annotations.NotNull; +import net.openhft.lang.model.constraints.NotNull; /** * @author peter.lawrey @@ -50,6 +50,12 @@ public enum StopCharTesters implements StopCharTester { public boolean isStopChar(int ch) { return ch <= 1; } + }, + ALL { + @Override + public boolean isStopChar(int ch) { + return ch < 0; + } }; @NotNull @@ -60,7 +66,7 @@ public enum StopCharTesters implements StopCharTester { } @NotNull - public static StopCharTester forChar(char ch) { + private static StopCharTester forChar(char ch) { return new CharCSTester(ch); } diff --git a/lang/src/main/java/net/openhft/lang/io/StringBuilderUtils.java b/lang/src/main/java/net/openhft/lang/io/StringBuilderUtils.java new file mode 100644 index 0000000..f11ed90 --- /dev/null +++ b/lang/src/main/java/net/openhft/lang/io/StringBuilderUtils.java @@ -0,0 +1,84 @@ +/* + * Copyright (C) 2015 higherfrequencytrading.com + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as published by + * the Free Software Foundation, either version 3 of the License. + * + * 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 Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with this program. If not, see <http://www.gnu.org/licenses/>. + */ + +package net.openhft.lang.io; + +import org.jetbrains.annotations.NotNull; + +import java.lang.reflect.Field; + +import static java.lang.Character.toLowerCase; + +/** + * Created by Rob Austin + */ +public enum StringBuilderUtils { + ; + + private static final Field SB_VALUE, SB_COUNT; + + static { + try { + SB_VALUE = Class.forName("java.lang.AbstractStringBuilder").getDeclaredField("value"); + SB_VALUE.setAccessible(true); + SB_COUNT = Class.forName("java.lang.AbstractStringBuilder").getDeclaredField("count"); + SB_COUNT.setAccessible(true); + } catch (Exception e) { + throw new AssertionError(e); + } + } + + public static boolean endsWith(@NotNull final CharSequence source, + @NotNull final String endsWith) { + for (int i = 1; i <= endsWith.length(); i++) { + if (toLowerCase(source.charAt(source.length() - i)) != + toLowerCase(endsWith.charAt(endsWith.length() - i))) { + return false; + } + } + + return true; + } + + public static boolean isEqual(CharSequence s, CharSequence cs) { + if (s == null) return false; + if (s.length() != cs.length()) return false; + for (int i = 0; i < cs.length(); i++) + if (s.charAt(i) != cs.charAt(i)) + return false; + return true; + } + + public static String toString(Object o) { + return o == null ? null : o.toString(); + } + + public static char[] extractChars(StringBuilder sb) { + try { + return (char[]) SB_VALUE.get(sb); + } catch (IllegalAccessException e) { + throw new AssertionError(e); + } + } + + public static void setCount(StringBuilder sb, int count) { + try { + SB_COUNT.setInt(sb, count); + } catch (IllegalAccessException e) { + throw new AssertionError(e); + } + } +} diff --git a/lang/src/main/java/net/openhft/lang/io/VanillaBytesHash.java b/lang/src/main/java/net/openhft/lang/io/VanillaBytesHash.java new file mode 100755 index 0000000..493ae2b --- /dev/null +++ b/lang/src/main/java/net/openhft/lang/io/VanillaBytesHash.java @@ -0,0 +1,89 @@ +package net.openhft.lang.io; + +import java.nio.ByteOrder; + +public enum VanillaBytesHash implements BytesHasher { + INSTANCE; + + public static final int K0 = 0x6d0f27bd; + public static final int K1 = 0xc1f3bfc9; + public static final int K2 = 0x6b192397; + public static final int K3 = 0x6b915657; + public static final int M0 = 0x5bc80bad; + public static final int M1 = 0xea7585d7; + public static final int M2 = 0x7a646e19; + public static final int M3 = 0x855dd4db; + private static final int HI_BYTES = ByteOrder.nativeOrder() == ByteOrder.LITTLE_ENDIAN ? 4 : 0; + + public static long agitate(long l) { + l += l >>> 22; + l ^= Long.rotateRight(l, 17); + return l; + } + + @Override + public long hash(Bytes bytes, long offset, long limit) { + long start = offset; + int remaining = (int) (limit - offset); + // use two hashes so that when they are combined the 64-bit hash is more random. + long h0 = (long) remaining * K0; + long h1 = 0, h2 = 0, h3 = 0; + int i; + // optimise chunks of 32 bytes but this is the same as the next loop. + for (i = 0; i < remaining - 31; i += 32) { + if (i > 0) { + h0 *= K0; + h1 *= K1; + h2 *= K2; + h3 *= K3; + } + long addrI = start + i; + long l0 = bytes.readLong(addrI); + int l0a = bytes.readInt(addrI + HI_BYTES); + long l1 = bytes.readLong(addrI + 8); + int l1a = bytes.readInt(addrI + 8 + HI_BYTES); + long l2 = bytes.readLong(addrI + 16); + int l2a = bytes.readInt(addrI + 16 + HI_BYTES); + long l3 = bytes.readLong(addrI + 24); + int l3a = bytes.readInt(addrI + 24 + HI_BYTES); + + h0 += (l0 + l1a - l2a) * M0; + h1 += (l1 + l2a - l3a) * M1; + h2 += (l2 + l3a - l0a) * M2; + h3 += (l3 + l0a - l1a) * M3; + } + + // perform a hash of the end. + int left = remaining - i; + if (left > 0) { + if (i > 0) { + h0 *= K0; + h1 *= K1; + h2 *= K2; + h3 *= K3; + } + + long addrI = start + i; + long l0 = bytes.readIncompleteLong(addrI); + int l0a = (int) (l0 >> 32); + long l1 = bytes.readIncompleteLong(addrI + 8); + int l1a = (int) (l1 >> 32); + long l2 = bytes.readIncompleteLong(addrI + 16); + int l2a = (int) (l2 >> 32); + long l3 = bytes.readIncompleteLong(addrI + 24); + int l3a = (int) (l3 >> 32); + + h0 += (l0 + l1a - l2a) * M0; + h1 += (l1 + l2a - l3a) * M1; + h2 += (l2 + l3a - l0a) * M2; + h3 += (l3 + l0a - l1a) * M3; + } + return agitate(h0) ^ agitate(h1) + ^ agitate(h2) ^ agitate(h3); + } + + @Override + public long hash(Bytes bytes) { + return hash(bytes, bytes.position(), bytes.limit()); + } +} diff --git a/lang/src/main/java/net/openhft/lang/io/VanillaBytesHasher.java b/lang/src/main/java/net/openhft/lang/io/VanillaBytesHasher.java new file mode 100644 index 0000000..774fc1a --- /dev/null +++ b/lang/src/main/java/net/openhft/lang/io/VanillaBytesHasher.java @@ -0,0 +1,46 @@ +/* + * Copyright (C) 2015 higherfrequencytrading.com + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as published by + * the Free Software Foundation, either version 3 of the License. + * + * 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 Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with this program. If not, see <http://www.gnu.org/licenses/>. + */ + +package net.openhft.lang.io; + +import net.openhft.lang.Maths; + +public enum VanillaBytesHasher implements BytesHasher { + INSTANCE; + private static final long LONG_LEVEL_PRIME_MULTIPLE = 0x9ddfea08eb382d69L; + private static final short SHORT_LEVEL_PRIME_MULTIPLE = 0x404f; + private static final byte BYTE_LEVEL_PRIME_MULTIPLE = 0x57; + + public long hash(Bytes bytes) { + return hash(bytes, bytes.position(), bytes.limit()); + } + + public long hash(Bytes bytes, long offset, long limit) { + return Maths.hash(limit - offset == 8 ? bytes.readLong(offset) : hash0(bytes, offset, limit)); + } + + private long hash0(Bytes bytes, long offset, long limit) { + long h = 0; + long i = offset; + for (; i < limit - 7; i += 8) + h = LONG_LEVEL_PRIME_MULTIPLE * h + bytes.readLong(i); + for (; i < limit - 1; i += 2) + h = SHORT_LEVEL_PRIME_MULTIPLE * h + bytes.readShort(i); + if (i < limit) + h = BYTE_LEVEL_PRIME_MULTIPLE * h + bytes.readByte(i); + return h; + } +} diff --git a/lang/src/main/java/net/openhft/lang/io/VanillaMappedBlocks.java b/lang/src/main/java/net/openhft/lang/io/VanillaMappedBlocks.java new file mode 100755 index 0000000..1cdd254 --- /dev/null +++ b/lang/src/main/java/net/openhft/lang/io/VanillaMappedBlocks.java @@ -0,0 +1,128 @@ +/* + * Copyright (C) 2015 higherfrequencytrading.com + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as published by + * the Free Software Foundation, either version 3 of the License. + * + * 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 Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with this program. If not, see <http://www.gnu.org/licenses/>. + */ +package net.openhft.lang.io; + +import java.io.File; +import java.io.IOException; +import java.util.ArrayList; +import java.util.List; + +public class VanillaMappedBlocks implements VanillaMappedResource { + private final VanillaMappedFile mappedFile; + private final List<VanillaMappedBytes> bytes; + private final long blockSize; + private final FileLifecycleListener fileLifecycleListener; + + private VanillaMappedBytes mb0; + private VanillaMappedBytes mb1; + + public VanillaMappedBlocks(final File path, VanillaMappedMode mode, long blockSize, long overlapSize) throws IOException { + this(path, mode, blockSize + overlapSize, null); + } + + public VanillaMappedBlocks(final File path, VanillaMappedMode mode, long blockSize, + FileLifecycleListener fileLifecycleListener) throws IOException { + this.fileLifecycleListener = fileLifecycleListener; + this.mappedFile = new VanillaMappedFile(path, mode, -1, fileLifecycleListener); + this.bytes = new ArrayList<VanillaMappedBytes>(); + this.blockSize = blockSize; + this.mb0 = null; + this.mb1 = null; + } + + public synchronized VanillaMappedBytes acquire(long index) throws IOException { + if (this.mb0 != null && this.mb0.index() == index) { + this.mb0.reserve(); + return this.mb0; + } + + if (this.mb1 != null && this.mb1.index() == index) { + this.mb1.reserve(); + return this.mb1; + } + + return acquire0(index); + } + + protected VanillaMappedBytes acquire0(long index) throws IOException { + + if (this.mb1 != null) { + this.mb1.release(); + } + + this.mb1 = this.mb0; + this.mb0 = this.mappedFile.bytes(index * this.blockSize, this.blockSize, index); + this.mb0.reserve(); + + bytes.add(this.mb0); + + for (int i = bytes.size() - 1; i >= 0; i--) { + if (bytes.get(i).unmapped()) { + bytes.remove(i); + } + } + + return this.mb0; + } + + @Override + public String path() { + return this.mappedFile.path(); + } + + @Override + public synchronized long size() { + return this.mappedFile.size(); + } + + @Override + public synchronized void close() throws IOException { + if (this.mb0 != null && !this.mb0.unmapped()) { + this.mb0.release(); + this.mb0 = null; + } + + if (this.mb1 != null && !this.mb1.unmapped()) { + this.mb1.release(); + this.mb1 = null; + } + + for (int i = bytes.size() - 1; i >= 0; i--) { + bytes.get(i).cleanup(); + } + + this.bytes.clear(); + this.mappedFile.close(); + } + + public static VanillaMappedBlocks readWrite(final File path, long size) throws IOException { + return readWrite(path, size, FileLifecycleListener.FileLifecycleListeners.IGNORE); + } + + public static VanillaMappedBlocks readOnly(final File path, long size) throws IOException { + return readOnly(path, size, FileLifecycleListener.FileLifecycleListeners.IGNORE); + } + + public static VanillaMappedBlocks readWrite(final File path, long size, + FileLifecycleListener listener) throws IOException { + return new VanillaMappedBlocks(path, VanillaMappedMode.RW, size, listener); + } + + public static VanillaMappedBlocks readOnly(final File path, long size, + FileLifecycleListener listener) throws IOException { + return new VanillaMappedBlocks(path, VanillaMappedMode.RO, size, listener); + } +} diff --git a/lang/src/main/java/net/openhft/lang/io/VanillaMappedBytes.java b/lang/src/main/java/net/openhft/lang/io/VanillaMappedBytes.java new file mode 100644 index 0000000..6f8191f --- /dev/null +++ b/lang/src/main/java/net/openhft/lang/io/VanillaMappedBytes.java @@ -0,0 +1,137 @@ +/* + * Copyright (C) 2015 higherfrequencytrading.com + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as published by + * the Free Software Foundation, either version 3 of the License. + * + * 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 Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with this program. If not, see <http://www.gnu.org/licenses/>. + */ +package net.openhft.lang.io; + +import sun.misc.Cleaner; +import sun.nio.ch.DirectBuffer; + +import java.io.File; +import java.io.IOException; +import java.nio.ByteBuffer; +import java.nio.MappedByteBuffer; +import java.nio.channels.FileChannel; + +public class VanillaMappedBytes extends NativeBytes { + private final File path; + private final MappedByteBuffer buffer; + private final FileChannel channel; + private final FileLifecycleListener fileLifecycleListener; + private final long index; + private boolean unmapped; + + public VanillaMappedBytes(final File path, final MappedByteBuffer buffer) { + this(path, buffer, -1, null, FileLifecycleListener.FileLifecycleListeners.IGNORE); + } + + public VanillaMappedBytes(final File path, final MappedByteBuffer buffer, FileLifecycleListener fileLifecycleListener) { + this(path, buffer, -1, null, fileLifecycleListener); + } + + public VanillaMappedBytes(final File path, final MappedByteBuffer buffer, long index) { + this(path, buffer, index, null, FileLifecycleListener.FileLifecycleListeners.IGNORE); + } + + public VanillaMappedBytes(final File path, final MappedByteBuffer buffer, long index, FileLifecycleListener fileLifecycleListener) { + this(path, buffer, index, null, fileLifecycleListener); + } + + protected VanillaMappedBytes(final File path, final MappedByteBuffer buffer, long index, final FileChannel channel) { + this(path, buffer, index, channel, FileLifecycleListener.FileLifecycleListeners.IGNORE); + } + + protected VanillaMappedBytes( + final File path, + final MappedByteBuffer buffer, + long index, + final FileChannel channel, + final FileLifecycleListener fileLifecycleListener) { + + super( + buffer.capacity() == 0 ? NO_PAGE : ((DirectBuffer) buffer).address(), + buffer.capacity() == 0 ? NO_PAGE : ((DirectBuffer) buffer).address() + buffer.capacity() + ); + + this.buffer = buffer; + this.path = path; + this.channel = channel; + this.unmapped = false; + this.index = index; + this.fileLifecycleListener = fileLifecycleListener; + } + + public long index() { + return this.index; + } + + public synchronized boolean unmapped() { + return this.unmapped; + } + + @Override + public boolean release() { + if(!unmapped()) { + return super.release(); + } + + return false; + } + + @Override + protected synchronized void cleanup() { + if(!this.unmapped) { + Cleaner cl = ((DirectBuffer)this.buffer).cleaner(); + if (cl != null) { + long start = System.nanoTime(); + cl.clean(); + + fileLifecycleListener.onEvent( + FileLifecycleListener.EventType.UNMAP, + this.path, + System.nanoTime() - start + ); + + } + + try { + if (this.channel != null && this.channel.isOpen()) { + this.channel.close(); + } + } catch(IOException e) { + throw new AssertionError(e); + } + + this.unmapped = true; + } + + super.cleanup(); + } + + public void force() { + long start = System.nanoTime(); + this.buffer.force(); + + fileLifecycleListener.onEvent( + FileLifecycleListener.EventType.SYNC, + this.path, + System.nanoTime() - start + ); + } + + @Override + public ByteBuffer sliceAsByteBuffer(ByteBuffer toReuse) { + return sliceAsByteBuffer(toReuse, buffer); + } +} diff --git a/lang/src/main/java/net/openhft/lang/io/VanillaMappedCache.java b/lang/src/main/java/net/openhft/lang/io/VanillaMappedCache.java new file mode 100755 index 0000000..4aa0511 --- /dev/null +++ b/lang/src/main/java/net/openhft/lang/io/VanillaMappedCache.java @@ -0,0 +1,141 @@ +/* + * Copyright (C) 2015 higherfrequencytrading.com + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as published by + * the Free Software Foundation, either version 3 of the License. + * + * 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 Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with this program. If not, see <http://www.gnu.org/licenses/>. + */ +package net.openhft.lang.io; + +import java.io.Closeable; +import java.io.File; +import java.io.IOException; +import java.util.Iterator; +import java.util.LinkedHashMap; +import java.util.Map; + +public class VanillaMappedCache<T> implements Closeable { + private final boolean cleanOnClose; + private final Map<T,VanillaMappedBytes> cache; + private final FileLifecycleListener fileLifecycleListener; + + public VanillaMappedCache() { + this(new LinkedHashMap<T, VanillaMappedBytes>(), false, FileLifecycleListener.FileLifecycleListeners.IGNORE); + } + + public VanillaMappedCache(final boolean cleanOnClose) { + this(new LinkedHashMap<T, VanillaMappedBytes>(), cleanOnClose, FileLifecycleListener.FileLifecycleListeners.IGNORE); + } + + public VanillaMappedCache(final int maximumCacheSize, boolean releaseOnRemove) { + this(maximumCacheSize, releaseOnRemove, false); + } + public VanillaMappedCache(final int maximumCacheSize, final boolean releaseOnRemove, final boolean cleanOnClose) { + this(maximumCacheSize, releaseOnRemove, cleanOnClose, FileLifecycleListener.FileLifecycleListeners.IGNORE); + } + + /** + * + * @param maximumCacheSize the maximum number of VanillaMappedBytes to cache + * @param releaseOnRemove release the VanillaMappedBytes when evicted from cache + * @param cleanOnClose clean the VanillaMappedBytes when evicted from cache + * @param fileLifecycleListener the file lifecycle + */ + public VanillaMappedCache( + final int maximumCacheSize, + final boolean releaseOnRemove, + final boolean cleanOnClose, + FileLifecycleListener fileLifecycleListener) { + + this(new LinkedHashMap<T, VanillaMappedBytes>(maximumCacheSize,1.0f,true) { + @Override + protected boolean removeEldestEntry(Map.Entry<T, VanillaMappedBytes> eldest) { + boolean removed = size() >= maximumCacheSize; + if (removed && releaseOnRemove) { + eldest.getValue().release(); + } + + return removed; + } + }, + cleanOnClose, + fileLifecycleListener); + } + + private VanillaMappedCache(final Map<T, VanillaMappedBytes> cache, final boolean cleanOnClose, FileLifecycleListener fileLifecycleListener) { + this.cache = cache; + this.cleanOnClose = cleanOnClose; + this.fileLifecycleListener = fileLifecycleListener; + } + + public VanillaMappedBytes get(T key) { + return this.cache.get(key); + } + + public VanillaMappedBytes put(T key, File path, long size) throws IOException { + return put(key,path,size,-1); + } + + public VanillaMappedBytes put(T key, File path, long size, long index) throws IOException { + VanillaMappedBytes data = this.cache.get(key); + + if(data != null) { + if (!data.unmapped()) { + data.cleanup(); + + throw new IllegalStateException( + "Buffer at " + data.index() + " has a count of " + + data.refCount() + ); + } + } + + data = VanillaMappedFile.readWriteBytes(path, size, index, fileLifecycleListener); + this.cache.put(key,data); + + return data; + } + + public int size() { + return this.cache.size(); + } + + @Override + public void close() { + final Iterator<Map.Entry<T,VanillaMappedBytes>> it = this.cache.entrySet().iterator(); + while(it.hasNext()) { + Map.Entry<T,VanillaMappedBytes> entry = it.next(); + entry.getValue().release(); + + if(this.cleanOnClose && !entry.getValue().unmapped()) { + entry.getValue().cleanup(); + entry.getValue().close(); + it.remove(); + + } else if(entry.getValue().unmapped()) { + entry.getValue().close(); + it.remove(); + } + } + + this.cache.clear(); + } + + public synchronized void checkCounts(int min, int max) { + for(VanillaMappedBytes data : this.cache.values()) { + if (data.refCount() < min || data.refCount() > max) { + throw new IllegalStateException( + "Buffer at " + data.index() + " has a count of " + + data.refCount() + ); + } + } + } +} + diff --git a/lang/src/main/java/net/openhft/lang/io/VanillaMappedFile.java b/lang/src/main/java/net/openhft/lang/io/VanillaMappedFile.java new file mode 100755 index 0000000..5562311 --- /dev/null +++ b/lang/src/main/java/net/openhft/lang/io/VanillaMappedFile.java @@ -0,0 +1,161 @@ +/* + * Copyright (C) 2015 higherfrequencytrading.com + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as published by + * the Free Software Foundation, either version 3 of the License. + * + * 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 Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with this program. If not, see <http://www.gnu.org/licenses/>. + */ +package net.openhft.lang.io; + +import net.openhft.lang.io.FileLifecycleListener.EventType; + +import java.io.File; +import java.io.IOException; +import java.io.RandomAccessFile; +import java.lang.reflect.InvocationTargetException; +import java.nio.ByteOrder; +import java.nio.MappedByteBuffer; +import java.nio.channels.FileChannel; + +/* + * Merge memory mapped files: + * - net.openhft.lang.io.MappedFile + * - net.openhft.lang.io.MappedStore + * - net.openhft.chronicle.VanillaFile + */ +public class VanillaMappedFile implements VanillaMappedResource { + + private final File path; + private final FileChannel fileChannel; + private final VanillaMappedMode mode; + private final long size; + private final FileLifecycleListener fileLifecycleListener; + + public VanillaMappedFile(final File path, VanillaMappedMode mode) throws IOException { + this(path, mode, -1, FileLifecycleListener.FileLifecycleListeners.IGNORE); + } + + public VanillaMappedFile(final File path, VanillaMappedMode mode, long size, + FileLifecycleListener fileLifecycleListener) throws IOException { + this.path = path; + this.mode = mode; + this.size = size; + this.fileChannel = fileChannel(path, mode, this.size, fileLifecycleListener); + this.fileLifecycleListener = fileLifecycleListener; + } + + public VanillaMappedBytes bytes(long address, long size) throws IOException { + return new VanillaMappedBytes(this.path, map(address, size), -1, null, this.fileLifecycleListener); + } + + public VanillaMappedBytes bytes(long address, long size, long index) throws IOException { + return new VanillaMappedBytes(this.path, map(address,size), index, null, this.fileLifecycleListener); + } + + @Override + public String path() { + return this.path.getAbsolutePath(); + } + + @Override + public long size() { + try { + return this.fileChannel.size(); + } catch (IOException e) { + return 0; + } + } + + @Override + public synchronized void close() throws IOException { + if(this.fileChannel.isOpen()) { + long start = System.nanoTime(); + this.fileChannel.close(); + this.fileLifecycleListener.onEvent(EventType.CLOSE, this.path, System.nanoTime() - start); + } + } + + // ************************************************************************* + // Helpers + // ************************************************************************* + + private synchronized MappedByteBuffer map(long address, long size) throws IOException { + long start = System.nanoTime(); + MappedByteBuffer buffer = this.fileChannel.map(this.mode.mapValue(),address,size); + buffer.order(ByteOrder.nativeOrder()); + fileLifecycleListener.onEvent(EventType.MMAP, path, System.nanoTime() - start); + return buffer; + } + + private static FileChannel fileChannel(final File path, VanillaMappedMode mapMode, long size, FileLifecycleListener fileLifecycleListener) throws IOException { + long start = System.nanoTime(); + FileChannel fileChannel = null; + try { + final RandomAccessFile raf = new RandomAccessFile(path, mapMode.stringValue()); + if (size > 0 && raf.length() != size) { + if (mapMode.mapValue() != FileChannel.MapMode.READ_WRITE) { + throw new IOException("Cannot resize file to " + size + " as mode is not READ_WRITE"); + } + + raf.setLength(size); + } + + fileChannel = raf.getChannel(); + } catch (Exception e) { + throw wrap(e); + } + + fileLifecycleListener.onEvent(EventType.NEW, path, System.nanoTime() - start); + return fileChannel; + } + + private static IOException wrap(Throwable throwable) { + if(throwable instanceof InvocationTargetException) { + throwable = throwable.getCause(); + + } else if(throwable instanceof IOException) { + return (IOException)throwable; + } + + return new IOException(throwable); + } + + public static VanillaMappedFile readWrite(final File path) throws IOException { + return new VanillaMappedFile(path,VanillaMappedMode.RW); + } + + public static VanillaMappedFile readWrite(final File path, long size) throws IOException { + return new VanillaMappedFile(path, VanillaMappedMode.RW, size, + FileLifecycleListener.FileLifecycleListeners.IGNORE); + } + + public static VanillaMappedFile readOnly(final File path) throws IOException { + return new VanillaMappedFile(path,VanillaMappedMode.RO); + } + + public static VanillaMappedFile readOnly(final File path, long size) throws IOException { + return new VanillaMappedFile(path, VanillaMappedMode.RO, size, + FileLifecycleListener.FileLifecycleListeners.IGNORE); + } + + public static VanillaMappedBytes readWriteBytes(final File path, long size) throws IOException { + return readWriteBytes(path, size, -1); + } + + public static VanillaMappedBytes readWriteBytes(final File path, long size, long index) throws IOException { + return readWriteBytes(path, size, index, FileLifecycleListener.FileLifecycleListeners.IGNORE); + } + + public static VanillaMappedBytes readWriteBytes(final File path, long size, long index, FileLifecycleListener fileLifecycleListener) throws IOException { + VanillaMappedFile vmf = new VanillaMappedFile(path, VanillaMappedMode.RW, -1, fileLifecycleListener); + return new VanillaMappedBytes(path, vmf.map(0,size), index, vmf.fileChannel, fileLifecycleListener); + } +} diff --git a/lang/src/main/java/net/openhft/lang/io/VanillaMappedMode.java b/lang/src/main/java/net/openhft/lang/io/VanillaMappedMode.java new file mode 100644 index 0000000..2fb6926 --- /dev/null +++ b/lang/src/main/java/net/openhft/lang/io/VanillaMappedMode.java @@ -0,0 +1,85 @@ +/* + * Copyright (C) 2015 higherfrequencytrading.com + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as published by + * the Free Software Foundation, either version 3 of the License. + * + * 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 Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with this program. If not, see <http://www.gnu.org/licenses/>. + */ +package net.openhft.lang.io; + +import java.nio.channels.FileChannel; + +/** + * Helper wrapper for mapeed access mode + */ +public enum VanillaMappedMode { + RO("r" ,0, FileChannel.MapMode.READ_ONLY), + RW("rw",1,FileChannel.MapMode.READ_WRITE) + ; + + private static final VanillaMappedMode[] VALUES = values(); + + private String stringValue; + private int intValue; + private FileChannel.MapMode mapValue; + + VanillaMappedMode(String stringValue, int intValue, FileChannel.MapMode mapValue) { + this.stringValue = stringValue; + this.intValue = intValue; + this.mapValue = mapValue; + } + + public int intValue() { + return this.intValue; + } + + public String stringValue() { + return this.stringValue; + } + + public FileChannel.MapMode mapValue() { + return this.mapValue; + } + + public static VanillaMappedMode defaultMode() { + return VanillaMappedMode.RO; + } + + public static VanillaMappedMode fromValue(int value) { + for(VanillaMappedMode mode : VALUES) { + if(mode.intValue() == value) { + return mode; + } + } + + return defaultMode(); + } + + public static VanillaMappedMode fromValue(String value) { + for(VanillaMappedMode mode : VALUES) { + if(mode.stringValue().equalsIgnoreCase(value)) { + return mode; + } + } + + return defaultMode(); + } + + public static VanillaMappedMode fromValue(FileChannel.MapMode value) { + for(VanillaMappedMode mode : VALUES) { + if(mode.mapValue() == value) { + return mode; + } + } + + return defaultMode(); + } +} diff --git a/lang/src/main/java/net/openhft/lang/io/VanillaMappedResource.java b/lang/src/main/java/net/openhft/lang/io/VanillaMappedResource.java new file mode 100644 index 0000000..e0a584a --- /dev/null +++ b/lang/src/main/java/net/openhft/lang/io/VanillaMappedResource.java @@ -0,0 +1,26 @@ +/* + * Copyright (C) 2015 higherfrequencytrading.com + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as published by + * the Free Software Foundation, either version 3 of the License. + * + * 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 Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with this program. If not, see <http://www.gnu.org/licenses/>. + */ +package net.openhft.lang.io; + +import java.io.IOException; + +public interface VanillaMappedResource { + String path(); + + long size(); + + void close() throws IOException; +} diff --git a/lang/src/main/java/net/openhft/lang/io/WrappedBytes.java b/lang/src/main/java/net/openhft/lang/io/WrappedBytes.java new file mode 100755 index 0000000..1176cea --- /dev/null +++ b/lang/src/main/java/net/openhft/lang/io/WrappedBytes.java @@ -0,0 +1,1056 @@ +/* + * Copyright (C) 2015 higherfrequencytrading.com + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as published by + * the Free Software Foundation, either version 3 of the License. + * + * 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 Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with this program. If not, see <http://www.gnu.org/licenses/>. + */ + +package net.openhft.lang.io; + +import net.openhft.lang.io.serialization.ObjectSerializer; +import net.openhft.lang.model.Byteable; +import net.openhft.lang.model.constraints.NotNull; +import org.jetbrains.annotations.Nullable; + +import java.io.File; +import java.io.InputStream; +import java.io.OutputStream; +import java.io.StreamCorruptedException; +import java.nio.BufferUnderflowException; +import java.nio.ByteBuffer; +import java.nio.ByteOrder; +import java.util.Collection; +import java.util.Map; + +/** + * A super class for those which wrap bytes. + */ +public abstract class WrappedBytes<B extends Bytes> implements Bytes { + protected B wrapped; + + protected WrappedBytes(B wrapped) { + this.wrapped = wrapped; + } + + @Override + public void clearThreadAssociation() { + wrapped.clearThreadAssociation(); + } + + public ByteBuffer sliceAsByteBuffer(@org.jetbrains.annotations.Nullable ByteBuffer toReuse) { + return wrapped.sliceAsByteBuffer(toReuse); + } + + public void readMarshallable(@NotNull Bytes in) throws IllegalStateException { + wrapped.readMarshallable(in); + } + + @Override + public String toDebugString(long limit) { + return wrapped.toDebugString(limit); + } + + @Override + public String toHexString(long limit) { + return wrapped.toHexString(limit); + } + + @Override + public boolean compare(long offset, RandomDataInput input, long inputOffset, long len) { + return wrapped.compare(offset, input, inputOffset, len); + } + + public long readCompactLong() { + return wrapped.readCompactLong(); + } + + public boolean tryLockNanosInt(long offset, long nanos) { + return wrapped.tryLockNanosInt(offset, nanos); + } + + public void writeMarshallable(@NotNull Bytes out) { + wrapped.writeMarshallable(out); + } + + public int readInt24() { + return wrapped.readInt24(); + } + + public void flush() { + wrapped.flush(); + } + + public void writeDouble(long offset, double v) { + wrapped.writeDouble(offset, v); + } + + public long limit() { + return wrapped.limit(); + } + + @NotNull + public ByteStringAppender appendTimeMillis(long timeInMS) { + return wrapped.appendTimeMillis(timeInMS); + } + + @org.jetbrains.annotations.Nullable + public <E extends Enum<E>> E parseEnum(@NotNull Class<E> eClass, @NotNull StopCharTester tester) throws BufferUnderflowException { + return wrapped.parseEnum(eClass, tester); + } + + public int refCount() { + return wrapped.refCount(); + } + + public void writeShort(long offset, int v) { + wrapped.writeShort(offset, v); + } + + public <E> void writeEnum(@org.jetbrains.annotations.Nullable E e) { + wrapped.writeEnum(e); + } + + @NotNull + public <E> ByteStringAppender append(@NotNull Iterable<E> list, @NotNull CharSequence separator) { + return wrapped.append(list, separator); + } + + public void writeCompactUnsignedShort(int v) { + wrapped.writeCompactUnsignedShort(v); + } + + public long readVolatileLong() { + return wrapped.readVolatileLong(); + } + + public void write(RandomDataInput in, long position, long length) { + wrapped.write(in, position, length); + } + + public void write(Byteable byteable) { + wrapped.write(byteable); + } + + public void writeOrderedInt(int v) { + wrapped.writeOrderedInt(v); + } + + public boolean readUTFΔ(@NotNull StringBuilder stringBuilder) { + return wrapped.readUTFΔ(stringBuilder); + } + + public void writeInt48(long offset, long v) { + wrapped.writeInt48(offset, v); + } + + public long readLong() { + return wrapped.readLong(); + } + + public long readIncompleteLong(long offset) { + return wrapped.readIncompleteLong(offset); + } + + public void writeLong(long v) { + wrapped.writeLong(v); + } + + @NotNull + public ByteStringAppender appendDateTimeMillis(long timeInMS) { + return wrapped.appendDateTimeMillis(timeInMS); + } + + @org.jetbrains.annotations.Nullable + public <E> E readEnum(@NotNull Class<E> eClass) { + return wrapped.readEnum(eClass); + } + + public void write(RandomDataInput in) { + wrapped.write(in); + } + + @NotNull + public ByteStringAppender append(double d) { + return wrapped.append(d); + } + + @NotNull + public String toDebugString() { + return wrapped.toDebugString(); + } + + public boolean isFinished() { + return wrapped.isFinished(); + } + + public void writeCompactUnsignedInt(long v) { + wrapped.writeCompactUnsignedInt(v); + } + + @NotNull + public ByteStringAppender append(double d, int precision) { + return wrapped.append(d, precision); + } + + public int readUnsignedByteOrThrow() throws BufferUnderflowException { + return wrapped.readUnsignedByteOrThrow(); + } + + public Bytes zeroOut(long start, long end) { + wrapped.zeroOut(start, end); + return this; + } + + @Override + public Bytes zeroOut(long start, long end, boolean ifNotZero) { + wrapped.zeroOut(start, end, ifNotZero); + return this; + } + + public void writeShort(int v) { + wrapped.writeShort(v); + } + + public short addShort(long offset, short s) { + return wrapped.addShort(offset, s); + } + + public void writeUnsignedInt(long v) { + wrapped.writeUnsignedInt(v); + } + + public void free() { + wrapped.free(); + } + + public int readUnsignedShort() { + return wrapped.readUnsignedShort(); + } + + public void writeStopBit(long n) { + wrapped.writeStopBit(n); + } + + @org.jetbrains.annotations.Nullable + public <T> T readObject(Class<T> tClass) throws IllegalStateException { + return wrapped.readObject(tClass); + } + + public void writeCompactInt(int v) { + wrapped.writeCompactInt(v); + } + + public void writeOrderedLong(long v) { + wrapped.writeOrderedLong(v); + } + + public byte addByte(long offset, byte b) { + return wrapped.addByte(offset, b); + } + + public int readVolatileInt() { + return wrapped.readVolatileInt(); + } + + public void close() { + wrapped.close(); + } + + public void read(@NotNull ByteBuffer bb) { + wrapped.read(bb); + } + + public void read(@NotNull ByteBuffer bb, int length) { + wrapped.read(bb, length); + } + + @NotNull + public ByteStringAppender append(long l, int base) { + return wrapped.append(l, base); + } + + public long skip(long n) { + return wrapped.skip(n); + } + + public boolean selfTerminating() { + return wrapped.selfTerminating(); + } + + public void writeBytes(@NotNull String s) { + wrapped.writeBytes(s); + } + + public long size() { + return wrapped.size(); + } + + public int readCompactUnsignedShort() { + return wrapped.readCompactUnsignedShort(); + } + + @NotNull + public ByteStringAppender append(@NotNull CharSequence s, int start, int end) { + return wrapped.append(s, start, end); + } + + public void writeCompactLong(long v) { + wrapped.writeCompactLong(v); + } + + public double readCompactDouble() { + return wrapped.readCompactDouble(); + } + + public void writeOrderedInt(long offset, int v) { + wrapped.writeOrderedInt(offset, v); + } + + public void writeObject(Object object, int start, int end) { + wrapped.writeObject(object, start, end); + } + + public CharSequence asString() { + return wrapped.asString(); + } + + @org.jetbrains.annotations.Nullable + public String readUTFΔ() { + return wrapped.readUTFΔ(); + } + + public Bytes flip() { + return wrapped.flip(); + } + + public int addInt(long offset, int i) { + return wrapped.addInt(offset, i); + } + + public long readUnsignedInt(long offset) { + return wrapped.readUnsignedInt(offset); + } + + public void writeByte(int v) { + wrapped.writeByte(v); + } + + public void writeUnsignedInt(long offset, long v) { + wrapped.writeUnsignedInt(offset, v); + } + + public void writeInt(int v) { + wrapped.writeInt(v); + } + + public short readShort() { + return wrapped.readShort(); + } + + public void writeUnsignedByte(long offset, int v) { + wrapped.writeUnsignedByte(offset, v); + } + + public void asString(Appendable appendable) { + wrapped.asString(appendable); + } + + public long readInt48(long offset) { + return wrapped.readInt48(offset); + } + + public void unlockRWReadLock(long offset) throws IllegalStateException { + wrapped.unlockRWReadLock(offset); + } + + @NotNull + public String readUTF() { + return wrapped.readUTF(); + } + + public void writeUnsignedShort(long offset, int v) { + wrapped.writeUnsignedShort(offset, v); + } + + public void readFully(@NotNull char[] data) { + wrapped.readFully(data); + } + + public void writeInt24(long offset, int v) { + wrapped.writeInt24(offset, v); + } + + public void writeChars(@NotNull CharSequence cs) { + wrapped.writeChars(cs); + } + + public float readFloat(long offset) { + return wrapped.readFloat(offset); + } + + public long capacity() { + return wrapped.capacity(); + } + + public CharSequence subSequence(int start, int end) { + return wrapped.subSequence(start, end); + } + + public Bytes clear() { + return wrapped.clear(); + } + + @org.jetbrains.annotations.Nullable + public String readUTFΔ(long offset) throws IllegalStateException { + return wrapped.readUTFΔ(offset); + } + + @NotNull + public ObjectSerializer objectSerializer() { + return wrapped.objectSerializer(); + } + + public void writeOrderedLong(long offset, long v) { + wrapped.writeOrderedLong(offset, v); + } + + public long addAtomicLong(long offset, long l) { + return wrapped.addAtomicLong(offset, l); + } + + @NotNull + public ByteStringAppender append(char c) { + return wrapped.append(c); + } + + public void busyLockInt(long offset) throws InterruptedException, IllegalStateException { + wrapped.busyLockInt(offset); + } + + public void resetLockInt(long offset) { + wrapped.resetLockInt(offset); + } + + @org.jetbrains.annotations.Nullable + public String readLine() { + return wrapped.readLine(); + } + + public char readChar(long offset) { + return wrapped.readChar(offset); + } + + @org.jetbrains.annotations.Nullable + public <T> T readInstance(@NotNull Class<T> objClass, T obj) { + return wrapped.readInstance(objClass, obj); + } + + @NotNull + public ByteStringAppender append(boolean b) { + return wrapped.append(b); + } + + public int addUnsignedByte(long offset, int i) { + return wrapped.addUnsignedByte(offset, i); + } + + public void readFully(@NotNull byte[] byteArray, int off, int len) { + wrapped.readFully(byteArray, off, len); + } + + public void readFully(@NotNull char[] data, int off, int len) { + wrapped.readFully(data, off, len); + } + + public int addAndGetInt(long offset, int delta) { + return wrapped.addAndGetInt(offset, delta); + } + + public long addUnsignedInt(long offset, long i) { + return wrapped.addUnsignedInt(offset, i); + } + + public void writeInt48(long v) { + wrapped.writeInt48(v); + } + + @NotNull + public ByteStringAppender append(@NotNull MutableDecimal md) { + return wrapped.append(md); + } + + public <K, V> Map<K, V> readMap(@NotNull Map<K, V> map, @NotNull Class<K> kClass, @NotNull Class<V> vClass) { + return wrapped.readMap(map, kClass, vClass); + } + + public char charAt(int index) { + return wrapped.charAt(index); + } + + public void writeOrderedFloat(long offset, float v) { + wrapped.writeOrderedFloat(offset, v); + } + + public void unlockRWWriteLock(long offset) throws IllegalStateException { + wrapped.unlockRWWriteLock(offset); + } + + public void parseUtf8(@NotNull StringBuilder builder, @NotNull StopCharTester tester) throws BufferUnderflowException { + wrapped.parseUtf8(builder, tester); + } + + @NotNull + public InputStream inputStream() { + return wrapped.inputStream(); + } + + public long remaining() { + return wrapped.remaining(); + } + + public void writeByte(long offset, int b) { + wrapped.writeByte(offset, b); + } + + public double readDouble() { + return wrapped.readDouble(); + } + + public int readCompactInt() { + return wrapped.readCompactInt(); + } + + public boolean release() { + return wrapped.release(); + } + + public boolean readBoolean(long offset) { + return wrapped.readBoolean(offset); + } + + public void writeBoolean(boolean v) { + wrapped.writeBoolean(v); + } + + public int read(@NotNull byte[] byteArray) { + return wrapped.read(byteArray); + } + + public void writeChars(@NotNull String s) { + wrapped.writeChars(s); + } + + public Bytes slice() { + return wrapped.slice(); + } + + public Bytes zeroOut() { + return wrapped.zeroOut(); + } + + public void toString(Appendable sb, long start, long position, long end) { + wrapped.toString(sb, start, position, end); + } + + public void writeOrderedDouble(long offset, double v) { + wrapped.writeOrderedDouble(offset, v); + } + + public long readStopBit() { + return wrapped.readStopBit(); + } + + public void busyLockLong(long offset) throws InterruptedException, IllegalStateException { + wrapped.busyLockLong(offset); + } + + public void writeDouble(double v) { + wrapped.writeDouble(v); + } + + public double readDouble(long offset) { + return wrapped.readDouble(offset); + } + + public float addFloat(long offset, float f) { + return wrapped.addFloat(offset, f); + } + + public boolean skipTo(@NotNull StopCharTester tester) { + return wrapped.skipTo(tester); + } + + public void writeChar(int v) { + wrapped.writeChar(v); + } + + public void writeInt(long offset, int v) { + wrapped.writeInt(offset, v); + } + + @NotNull + public OutputStream outputStream() { + return wrapped.outputStream(); + } + + public boolean compareAndSwapDouble(long offset, double expected, double x) { + return wrapped.compareAndSwapDouble(offset, expected, x); + } + + public File file() { + return wrapped.file(); + } + + public <E> void readList(@NotNull Collection<E> list, @NotNull Class<E> eClass) { + wrapped.readList(list, eClass); + } + + public void writeUnsignedByte(int v) { + wrapped.writeUnsignedByte(v); + } + + public int readInt24(long offset) { + return wrapped.readInt24(offset); + } + + public long readInt48() { + return wrapped.readInt48(); + } + + public void write(@NotNull char[] data) { + wrapped.write(data); + } + + @org.jetbrains.annotations.Nullable + public Object readObject() throws IllegalStateException { + return wrapped.readObject(); + } + + @NotNull + public ByteStringAppender append(@net.openhft.lang.model.constraints.Nullable Enum value) { + return wrapped.append(value); + } + + @NotNull + public String parseUtf8(@NotNull StopCharTester tester) throws BufferUnderflowException { + return wrapped.parseUtf8(tester); + } + + public int readInt() { + return wrapped.readInt(); + } + + public void write(@NotNull char[] data, int off, int len) { + wrapped.write(data, off, len); + } + + public int addUnsignedShort(long offset, int i) { + return wrapped.addUnsignedShort(offset, i); + } + + public float readFloat() { + return wrapped.readFloat(); + } + + public int available() { + return wrapped.available(); + } + + public long position() { + return wrapped.position(); + } + + public double addDouble(long offset, double d) { + return wrapped.addDouble(offset, d); + } + + public void write(int b) { + wrapped.write(b); + } + + public int skipBytes(int n) { + return wrapped.skipBytes(n); + } + + public short readCompactShort() { + return wrapped.readCompactShort(); + } + + public void write(long offset, byte[] byteArray) { + wrapped.write(offset, byteArray); + } + + public <E> void writeList(@NotNull Collection<E> list) { + wrapped.writeList(list); + } + + public int read(@NotNull byte[] byteArray, int off, int len) { + return wrapped.read(byteArray, off, len); + } + + public int readInt(long offset) { + return wrapped.readInt(offset); + } + + public void writeFloat(long offset, float v) { + wrapped.writeFloat(offset, v); + } + + public long parseLong() throws BufferUnderflowException { + return wrapped.parseLong(); + } + + public int readUnsignedByte(long offset) { + return wrapped.readUnsignedByte(offset); + } + + public Bytes slice(long offset, long length) { + return wrapped.slice(offset, length); + } + + public void writeObject(@org.jetbrains.annotations.Nullable Object object) { + wrapped.writeObject(object); + } + + public int length() { + return wrapped.length(); + } + + public char readChar() { + return wrapped.readChar(); + } + + public int read() { + return wrapped.read(); + } + + public void writeBoolean(long offset, boolean v) { + wrapped.writeBoolean(offset, v); + } + + public double parseDouble() throws BufferUnderflowException { + return wrapped.parseDouble(); + } + + public void writeCompactDouble(double v) { + wrapped.writeCompactDouble(v); + } + + public float addAtomicFloat(long offset, float f) { + return wrapped.addAtomicFloat(offset, f); + } + + public void selfTerminating(boolean selfTerminate) { + wrapped.selfTerminating(selfTerminate); + } + + public long readCompactUnsignedInt() { + return wrapped.readCompactUnsignedInt(); + } + + public double readVolatileDouble(long offset) { + return wrapped.readVolatileDouble(offset); + } + + public long addLong(long offset, long i) { + return wrapped.addLong(offset, i); + } + + public long readLong(long offset) { + return wrapped.readLong(offset); + } + + public boolean compareAndSwapInt(long offset, int expected, int x) { + return wrapped.compareAndSwapInt(offset, expected, x); + } + + @NotNull + public ByteStringAppender append(@NotNull CharSequence s) { + return wrapped.append(s); + } + + @NotNull + public ByteStringAppender append(int i) { + return wrapped.append(i); + } + + public <K, V> void writeMap(@NotNull Map<K, V> map) { + wrapped.writeMap(map); + } + + public Boolean parseBoolean(@NotNull StopCharTester tester) throws BufferUnderflowException { + return wrapped.parseBoolean(tester); + } + + public boolean tryRWReadLock(long offset, long timeOutNS) throws IllegalStateException, InterruptedException { + return wrapped.tryRWReadLock(offset, timeOutNS); + } + + public int readUnsignedShort(long offset) { + return wrapped.readUnsignedShort(offset); + } + + public void writeUTFΔ(long offset, int maxSize, @org.jetbrains.annotations.Nullable CharSequence s) throws IllegalStateException { + wrapped.writeUTFΔ(offset, maxSize, s); + } + + public byte readByte(long offset) { + return wrapped.readByte(offset); + } + + @NotNull + public ByteStringAppender append(long l) { + return wrapped.append(l); + } + + public void writeUTFΔ(@org.jetbrains.annotations.Nullable CharSequence s) { + wrapped.writeUTFΔ(s); + } + + public boolean compareAndSwapLong(long offset, long expected, long x) { + return wrapped.compareAndSwapLong(offset, expected, x); + } + + public void writeCompactShort(int v) { + wrapped.writeCompactShort(v); + } + + public Bytes bytes() { + return wrapped.bytes(); + } + + public void write(byte[] byteArray) { + wrapped.write(byteArray); + } + + public void unlockInt(long offset) throws IllegalMonitorStateException { + wrapped.unlockInt(offset); + } + + public boolean tryLockLong(long offset) { + return wrapped.tryLockLong(offset); + } + + public byte readByte() { + return wrapped.readByte(); + } + + public boolean tryRWWriteLock(long offset, long timeOutNS) throws IllegalStateException, InterruptedException { + return wrapped.tryRWWriteLock(offset, timeOutNS); + } + + public void write(byte[] byteArray, int off, int len) { + wrapped.write(byteArray, off, len); + } + + public void writeUTF(@NotNull String s) { + wrapped.writeUTF(s); + } + + public Bytes load() { + return wrapped.load(); + } + + public int getAndAdd(long offset, int delta) { + return wrapped.getAndAdd(offset, delta); + } + + public short readShort(long offset) { + return wrapped.readShort(offset); + } + + public boolean stepBackAndSkipTo(@NotNull StopCharTester tester) { + return wrapped.stepBackAndSkipTo(tester); + } + + public void resetLockLong(long offset) { + wrapped.resetLockLong(offset); + } + + public int readVolatileInt(long offset) { + return wrapped.readVolatileInt(offset); + } + + @NotNull + public ByteOrder byteOrder() { + return wrapped.byteOrder(); + } + + public Bytes bytes(long offset, long length) { + return wrapped.bytes(offset, length); + } + + public void alignPositionAddr(int alignment) { + wrapped.alignPositionAddr(alignment); + } + + public void writeUnsignedShort(int v) { + wrapped.writeUnsignedShort(v); + } + + public long parseLong(int base) throws BufferUnderflowException { + return wrapped.parseLong(base); + } + + public boolean readBoolean() { + return wrapped.readBoolean(); + } + + public void checkEndOfBuffer() throws IndexOutOfBoundsException { + wrapped.checkEndOfBuffer(); + } + + public float readVolatileFloat(long offset) { + return wrapped.readVolatileFloat(offset); + } + + @NotNull + public MutableDecimal parseDecimal(@NotNull MutableDecimal decimal) throws BufferUnderflowException { + return wrapped.parseDecimal(decimal); + } + + public double addAtomicDouble(long offset, double d) { + return wrapped.addAtomicDouble(offset, d); + } + + public void unlockLong(long offset) throws IllegalMonitorStateException { + wrapped.unlockLong(offset); + } + + public void writeFloat(float v) { + wrapped.writeFloat(v); + } + + public void reserve() { + wrapped.reserve(); + } + + public void write(@NotNull ByteBuffer bb) { + wrapped.write(bb); + } + + public long threadIdForLockLong(long offset) { + return wrapped.threadIdForLockLong(offset); + } + + public void writeChar(long offset, int v) { + wrapped.writeChar(offset, v); + } + + public boolean tryLockNanosLong(long offset, long nanos) { + return wrapped.tryLockNanosLong(offset, nanos); + } + + public int addAtomicInt(long offset, int i) { + return wrapped.addAtomicInt(offset, i); + } + + public <OBJ> void writeInstance(@NotNull Class<OBJ> objClass, @NotNull OBJ obj) { + wrapped.writeInstance(objClass, obj); + } + + public void readFully(@NotNull byte[] byteArray) { + wrapped.readFully(byteArray); + } + + public Bytes position(long position) { + return wrapped.position(position); + } + + public void writeLong(long offset, long v) { + wrapped.writeLong(offset, v); + } + + public void readObject(Object object, int start, int end) { + wrapped.readObject(object, start, end); + } + + public int threadIdForLockInt(long offset) { + return wrapped.threadIdForLockInt(offset); + } + + @NotNull + public ByteStringAppender appendDateMillis(long timeInMS) { + return wrapped.appendDateMillis(timeInMS); + } + + public void writeInt24(int v) { + wrapped.writeInt24(v); + } + + public boolean startsWith(RandomDataInput keyBytes) { + return wrapped.startsWith(keyBytes); + } + + public long readUnsignedInt() { + return wrapped.readUnsignedInt(); + } + + public Bytes limit(long limit) { + return wrapped.limit(limit); + } + + public void finish() { + wrapped.finish(); + } + + public long address() { + return wrapped.address(); + } + + public boolean tryLockInt(long offset) { + return wrapped.tryLockInt(offset); + } + + public long readVolatileLong(long offset) { + return wrapped.readVolatileLong(offset); + } + + public int readUnsignedByte() { + return wrapped.readUnsignedByte(); + } + + @Override + public void readFully(long offset, @org.jetbrains.annotations.NotNull byte[] byteArray, int off, int len) { + wrapped.readFully(offset, byteArray, off, len); + } + + @Override + public void write(long offset, byte[] byteArray, int off, int len) { + wrapped.write(offset, byteArray, off, len); + } + + @Override + public void write(long offset, Bytes bytes) { + wrapped.write(offset, bytes); + } + + @Override + public boolean read8bitText(@org.jetbrains.annotations.NotNull StringBuilder stringBuilder) throws StreamCorruptedException { + return wrapped.read8bitText(stringBuilder); + } + + @Override + public <E> E readEnum(long offset, int maxSize, Class<E> eClass) { + return wrapped.readEnum(offset, maxSize, eClass); + } + + @Override + public void write8bitText(@Nullable CharSequence s) { + wrapped.write8bitText(s); + } + + @Override + public void writeEnum(long offset, int len, Object object) { + wrapped.writeEnum(offset, len, object); + } +} diff --git a/lang/src/main/java/net/openhft/lang/io/serialization/BytesMarshallable.java b/lang/src/main/java/net/openhft/lang/io/serialization/BytesMarshallable.java index a92b316..5ac643e 100644 --- a/lang/src/main/java/net/openhft/lang/io/serialization/BytesMarshallable.java +++ b/lang/src/main/java/net/openhft/lang/io/serialization/BytesMarshallable.java @@ -1,23 +1,23 @@ /* - * Copyright 2013 Peter Lawrey + * Copyright (C) 2015 higherfrequencytrading.com * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as published by + * the Free Software Foundation, either version 3 of the License. * - * http://www.apache.org/licenses/LICENSE-2.0 + * 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 Lesser General Public License for more details. * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. + * You should have received a copy of the GNU Lesser General Public License + * along with this program. If not, see <http://www.gnu.org/licenses/>. */ package net.openhft.lang.io.serialization; import net.openhft.lang.io.Bytes; -import org.jetbrains.annotations.NotNull; +import net.openhft.lang.model.constraints.NotNull; /** * Integrated marshaller for objects. diff --git a/lang/src/main/java/net/openhft/lang/io/serialization/BytesMarshallableSerializer.java b/lang/src/main/java/net/openhft/lang/io/serialization/BytesMarshallableSerializer.java new file mode 100644 index 0000000..d68e355 --- /dev/null +++ b/lang/src/main/java/net/openhft/lang/io/serialization/BytesMarshallableSerializer.java @@ -0,0 +1,196 @@ +/* + * Copyright (C) 2015 higherfrequencytrading.com + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as published by + * the Free Software Foundation, either version 3 of the License. + * + * 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 Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with this program. If not, see <http://www.gnu.org/licenses/>. + */ + +package net.openhft.lang.io.serialization; + +import net.openhft.lang.io.AbstractBytes; +import net.openhft.lang.io.Bytes; +import net.openhft.lang.io.NativeBytes; +import net.openhft.lang.io.serialization.impl.NoMarshaller; +import net.openhft.lang.io.serialization.impl.StringBuilderPool; +import net.openhft.lang.io.serialization.impl.VanillaBytesMarshallerFactory; +import net.openhft.lang.model.constraints.NotNull; +import net.openhft.lang.pool.EnumInterner; + +import java.io.Externalizable; +import java.io.IOException; + +/** + * An extension of built-in Java serialization, featuring special treatment of {@link + * BytesMarshallable} objects, compact {@link String} encoding and support of pluggable custom + * serializers for arbitrary classes. + * + * <p>{@code BytesMarshallableSerializer} could benefit if objects (either top-level serialized or + * nested fields) implement {@link BytesMarshallable} interface the same way as built-in + * serialization benefit if objects implement {@link Externalizable} (of cause, {@code + * BytesMarshallableSerializer} supports {@code Externalizable} too). + * + * <p>{@link CharSequence}s, including {@code String}s (either top-level serialized or nested + * fields) are serialized in UTF-8 encoding. + * + * <p>Custom per-class serializers are held by {@link BytesMarshallerFactory}, which could be + * passed via constructor or static factory {@link #create create()} method. + * + * @see #create(BytesMarshallerFactory, ObjectSerializer) + */ +public class BytesMarshallableSerializer implements ObjectSerializer { + private static final long serialVersionUID = 0L; + + private static final byte NULL = 'N'; + private static final byte ENUMED = 'E'; + private static final byte SERIALIZED = 'S'; + private static final StringBuilderPool SBP = new StringBuilderPool(); + + private final BytesMarshallerFactory bytesMarshallerFactory; + private final ObjectSerializer objectSerializer; + + protected BytesMarshallableSerializer(BytesMarshallerFactory bytesMarshallerFactory, ObjectSerializer objectSerializer) { + this.bytesMarshallerFactory = bytesMarshallerFactory; + this.objectSerializer = objectSerializer; + } + + @Override + public void writeSerializable(Bytes bytes, Object object, Class expectedClass) throws IOException { + if (object == null) { + bytes.writeByte(NULL); + return; + } + if (expectedClass != null) { + if (BytesMarshallable.class.isAssignableFrom(expectedClass)) { + ((BytesMarshallable) object).writeMarshallable(bytes); + return; + + } else if (Externalizable.class.isAssignableFrom(expectedClass)) { + ((Externalizable) object).writeExternal(bytes); + return; + + } else if (CharSequence.class.isAssignableFrom(expectedClass)) { + bytes.writeUTFΔ((CharSequence) object); + return; + + } else if (Enum.class.isAssignableFrom(expectedClass)) { + bytes.write8bitText(object.toString()); + return; + } + } + writeSerializable2(bytes, object); + } + + private void writeSerializable2(Bytes bytes, Object object) throws IOException { + Class<?> clazz = object.getClass(); + BytesMarshaller em = bytesMarshallerFactory.acquireMarshaller(clazz, false); + if (em == NoMarshaller.INSTANCE && autoGenerateMarshaller(object)) + em = bytesMarshallerFactory.acquireMarshaller(clazz, true); + + if (em != NoMarshaller.INSTANCE) { + if (em instanceof CompactBytesMarshaller) { + bytes.writeByte(((CompactBytesMarshaller) em).code()); + em.write(bytes, object); + return; + } + bytes.writeByte(ENUMED); + this.writeSerializable(bytes, clazz, Class.class); + em.write(bytes, object); + return; + } + bytes.writeByte(SERIALIZED); + // TODO this is the lame implementation, but it works. + objectSerializer.writeSerializable(bytes, object, null); + } + + static boolean autoGenerateMarshaller(Object obj) { + return (obj instanceof Comparable && obj.getClass().getPackage().getName().startsWith("java")) + || obj instanceof Externalizable + || obj instanceof BytesMarshallable; + } + + @Override + public <T> T readSerializable(@NotNull Bytes bytes, Class<T> expectedClass, T object) throws IOException, ClassNotFoundException { + if (expectedClass != null) { + try { + if (BytesMarshallable.class.isAssignableFrom(expectedClass)) { + return readBytesMarshallable(bytes, expectedClass, object); + + } else if (Externalizable.class.isAssignableFrom(expectedClass)) { + return readExternalizable(bytes, expectedClass, object); + + } else if (CharSequence.class.isAssignableFrom(expectedClass)) { + return readCharSequence(bytes, object); + + } else if (Enum.class.isAssignableFrom(expectedClass)) { + StringBuilder sb = SBP.acquireStringBuilder(); + bytes.read8bitText(sb); + return (T) EnumInterner.intern((Class<Enum>) expectedClass, sb); + } + } catch (InstantiationException e) { + throw new IOException("Unable to create " + expectedClass, e); + } + } + int type = bytes.readUnsignedByteOrThrow(); + switch (type) { + case AbstractBytes.END_OF_BUFFER: + case NULL: + return null; + case ENUMED: { + Class clazz = this.readSerializable(bytes, Class.class, null); + assert clazz != null; + return (T) bytesMarshallerFactory.acquireMarshaller(clazz, true).read(bytes, object); + } + + case SERIALIZED: { + return objectSerializer.readSerializable(bytes, expectedClass, object); + } + + default: + BytesMarshaller<Object> m = bytesMarshallerFactory.getMarshaller((byte) type); + if (m == null) + throw new IllegalStateException("Unknown type " + (char) type); + return (T) m.read(bytes); + } + } + + private <T> T readCharSequence(Bytes bytes, T object) { + if (object instanceof StringBuilder) { + bytes.readUTFΔ(((StringBuilder) object)); + return object; + + } else { + return (T) bytes.readUTFΔ(); + } + } + + private <T> T readExternalizable(Bytes bytes, Class<T> expectedClass, T object) throws InstantiationException, IOException, ClassNotFoundException { + if (object == null) + object = (T) NativeBytes.UNSAFE.allocateInstance(expectedClass); + ((Externalizable) object).readExternal(bytes); + return object; + } + + private <T> T readBytesMarshallable(Bytes bytes, Class<T> expectedClass, T object) throws InstantiationException { + if (object == null) + object = (T) NativeBytes.UNSAFE.allocateInstance(expectedClass); + ((BytesMarshallable) object).readMarshallable(bytes); + return object; + } + + public static ObjectSerializer create() { + return create(new VanillaBytesMarshallerFactory(), JDKZObjectSerializer.INSTANCE); + } + + public static ObjectSerializer create(BytesMarshallerFactory bytesMarshallerFactory, ObjectSerializer instance) { + return bytesMarshallerFactory == null ? instance : new BytesMarshallableSerializer(bytesMarshallerFactory, instance); + } +} diff --git a/lang/src/main/java/net/openhft/lang/io/serialization/BytesMarshaller.java b/lang/src/main/java/net/openhft/lang/io/serialization/BytesMarshaller.java index d9a4fa2..d2593aa 100644 --- a/lang/src/main/java/net/openhft/lang/io/serialization/BytesMarshaller.java +++ b/lang/src/main/java/net/openhft/lang/io/serialization/BytesMarshaller.java @@ -1,45 +1,60 @@ /* - * Copyright 2013 Peter Lawrey + * Copyright (C) 2015 higherfrequencytrading.com * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as published by + * the Free Software Foundation, either version 3 of the License. * - * http://www.apache.org/licenses/LICENSE-2.0 + * 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 Lesser General Public License for more details. * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. + * You should have received a copy of the GNU Lesser General Public License + * along with this program. If not, see <http://www.gnu.org/licenses/>. */ package net.openhft.lang.io.serialization; import net.openhft.lang.io.Bytes; -import org.jetbrains.annotations.Nullable; +import net.openhft.lang.model.constraints.Nullable; + +import java.io.Serializable; /** - * External marshaller for classes. + * External marshaller for classes. From design patterns point of view, this interface + * is marshalling <i>strategy</i>. * * @author peter.lawrey * @see BytesMarshallable */ -public interface BytesMarshaller<E> { +public interface BytesMarshaller<E> extends Serializable { + /** - * write the object out as bytes. + * Write the object out to the {@code bytes}. * * @param bytes to write to - * @param e to write + * @param e the object to write */ void write(Bytes bytes, E e); /** - * Read bytes and obtain an object + * Reads and returns an object from {@code bytes}. * * @param bytes to read - * @return the object + * @return the read object */ @Nullable E read(Bytes bytes); + + /** + * Reads and returns an object from {@code bytes}, reusing the given object, if possible. + * + * @param bytes to read + * @param e an object to reuse, if possible. {@code null} could be passed, in this case + * a new object should be allocated anyway. + * @return the read object + */ + @Nullable + E read(Bytes bytes, @Nullable E e); } diff --git a/lang/src/main/java/net/openhft/lang/io/serialization/BytesMarshallerFactory.java b/lang/src/main/java/net/openhft/lang/io/serialization/BytesMarshallerFactory.java index 87218b7..6814fe8 100644 --- a/lang/src/main/java/net/openhft/lang/io/serialization/BytesMarshallerFactory.java +++ b/lang/src/main/java/net/openhft/lang/io/serialization/BytesMarshallerFactory.java @@ -1,27 +1,29 @@ /* - * Copyright 2013 Peter Lawrey + * Copyright (C) 2015 higherfrequencytrading.com * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as published by + * the Free Software Foundation, either version 3 of the License. * - * http://www.apache.org/licenses/LICENSE-2.0 + * 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 Lesser General Public License for more details. * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. + * You should have received a copy of the GNU Lesser General Public License + * along with this program. If not, see <http://www.gnu.org/licenses/>. */ package net.openhft.lang.io.serialization; -import org.jetbrains.annotations.NotNull; +import net.openhft.lang.model.constraints.NotNull; + +import java.io.Serializable; /** * @author peter.lawrey */ -public interface BytesMarshallerFactory { +public interface BytesMarshallerFactory extends Serializable { @NotNull <E> BytesMarshaller<E> acquireMarshaller(@NotNull Class<E> eClass, boolean create); diff --git a/lang/src/main/java/net/openhft/lang/io/serialization/CompactBytesMarshaller.java b/lang/src/main/java/net/openhft/lang/io/serialization/CompactBytesMarshaller.java index 6ee1f87..5b8ef29 100644 --- a/lang/src/main/java/net/openhft/lang/io/serialization/CompactBytesMarshaller.java +++ b/lang/src/main/java/net/openhft/lang/io/serialization/CompactBytesMarshaller.java @@ -1,17 +1,17 @@ /* - * Copyright 2013 Peter Lawrey + * Copyright (C) 2015 higherfrequencytrading.com * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as published by + * the Free Software Foundation, either version 3 of the License. * - * http://www.apache.org/licenses/LICENSE-2.0 + * 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 Lesser General Public License for more details. * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. + * You should have received a copy of the GNU Lesser General Public License + * along with this program. If not, see <http://www.gnu.org/licenses/>. */ package net.openhft.lang.io.serialization; @@ -20,8 +20,21 @@ package net.openhft.lang.io.serialization; * A BytesMarshaller with a byte code for the class. */ public interface CompactBytesMarshaller<E> extends BytesMarshaller<E> { + byte BYTE_BUFFER_CODE = 'B' & 31; + byte CLASS_CODE = 'C' & 31; + byte INT_CODE = 'I' & 31; + byte LONG_CODE = 'L' & 31; + byte DOUBLE_CODE = 'D' & 31; + byte DATE_CODE = 'T' & 31; + byte STRING_CODE = 'S' & 31; + byte STRINGZ_MAP_CODE = 'Y' & 31; // compressed string. + byte STRINGZ_CODE = 'Z' & 31; // compressed string. + byte LIST_CODE = '['; + byte SET_CODE = '[' & 31; + byte MAP_CODE = '{'; + /** - * the code for this marshaller + * @return the code for this marshaller */ byte code(); } diff --git a/lang/src/main/java/net/openhft/lang/io/serialization/JDKObjectSerializer.java b/lang/src/main/java/net/openhft/lang/io/serialization/JDKObjectSerializer.java new file mode 100644 index 0000000..30557fe --- /dev/null +++ b/lang/src/main/java/net/openhft/lang/io/serialization/JDKObjectSerializer.java @@ -0,0 +1,38 @@ +/* + * Copyright (C) 2015 higherfrequencytrading.com + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as published by + * the Free Software Foundation, either version 3 of the License. + * + * 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 Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with this program. If not, see <http://www.gnu.org/licenses/>. + */ + +package net.openhft.lang.io.serialization; + +import net.openhft.lang.io.Bytes; +import net.openhft.lang.model.constraints.NotNull; + +import java.io.IOException; +import java.io.ObjectInputStream; +import java.io.ObjectOutputStream; + +public enum JDKObjectSerializer implements ObjectSerializer { + INSTANCE; + + @Override + public void writeSerializable(Bytes bytes, Object object, Class expectedClass) throws IOException { + new ObjectOutputStream(bytes.outputStream()).writeObject(object); + } + + @Override + public <T> T readSerializable(@NotNull Bytes bytes, Class<T> expectedClass, T object) throws IOException, ClassNotFoundException { + return (T) new ObjectInputStream(bytes.inputStream()).readObject(); + } +} diff --git a/lang/src/main/java/net/openhft/lang/io/serialization/JDKZObjectSerializer.java b/lang/src/main/java/net/openhft/lang/io/serialization/JDKZObjectSerializer.java new file mode 100644 index 0000000..784e7a9 --- /dev/null +++ b/lang/src/main/java/net/openhft/lang/io/serialization/JDKZObjectSerializer.java @@ -0,0 +1,71 @@ +/* + * Copyright (C) 2015 higherfrequencytrading.com + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as published by + * the Free Software Foundation, either version 3 of the License. + * + * 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 Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with this program. If not, see <http://www.gnu.org/licenses/>. + */ + +package net.openhft.lang.io.serialization; + +import net.openhft.lang.io.Bytes; +import net.openhft.lang.model.constraints.NotNull; + +import java.io.*; +import java.util.zip.DeflaterOutputStream; +import java.util.zip.InflaterInputStream; + +public enum JDKZObjectSerializer implements ObjectSerializer { + INSTANCE; + + @Override + public void writeSerializable(Bytes bytes, Object object, Class expectedClass) throws IOException { + // reset the finished flag and append + long position = bytes.position(); + bytes.clear(); + bytes.position(position + 4); + OutputStream out = bytes.outputStream(); + ObjectOutputStream oos = new ObjectOutputStream(new DeflaterOutputStream(out)); + oos.writeObject(object); + oos.close(); + long length = bytes.position() - position - 4; + bytes.writeUnsignedInt(position, length); + } + + @Override + public <T> T readSerializable(@NotNull Bytes bytes, Class<T> expectedClass, T object) throws IOException, ClassNotFoundException { + long length = bytes.readUnsignedInt(); + if (length < 8 || length > Integer.MAX_VALUE) + throw new StreamCorruptedException("length = " + Long.toHexString(length)); + long end = bytes.position() + length; + long lim = bytes.limit(); + bytes.limit(end); + int magic = bytes.readUnsignedShort(bytes.position()); + InputStream in = bytes.inputStream(); + switch (magic) { + case 0xEDAC: + break; + + case 0x9c78: + in = new InflaterInputStream(in); + break; + default: + throw new StreamCorruptedException("Unknown magic number " + Integer.toHexString(magic)); + } + T t = (T) new ObjectInputStream(in).readObject(); + bytes.limit(lim); + if (end != bytes.position()) { + System.out.println("diff: " + (end - bytes.position())); + bytes.position(end); + } + return t; + } +} diff --git a/lang/src/main/java/net/openhft/lang/io/serialization/ObjectFactory.java b/lang/src/main/java/net/openhft/lang/io/serialization/ObjectFactory.java new file mode 100644 index 0000000..40dea33 --- /dev/null +++ b/lang/src/main/java/net/openhft/lang/io/serialization/ObjectFactory.java @@ -0,0 +1,23 @@ +/* + * Copyright (C) 2015 higherfrequencytrading.com + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as published by + * the Free Software Foundation, either version 3 of the License. + * + * 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 Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with this program. If not, see <http://www.gnu.org/licenses/>. + */ + +package net.openhft.lang.io.serialization; + +import java.io.Serializable; + +public interface ObjectFactory<E> extends Serializable { + E create() throws InstantiationException, IllegalAccessException; +} diff --git a/lang/src/main/java/net/openhft/lang/io/serialization/ObjectSerializer.java b/lang/src/main/java/net/openhft/lang/io/serialization/ObjectSerializer.java new file mode 100644 index 0000000..7683862 --- /dev/null +++ b/lang/src/main/java/net/openhft/lang/io/serialization/ObjectSerializer.java @@ -0,0 +1,65 @@ +/* + * Copyright (C) 2015 higherfrequencytrading.com + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as published by + * the Free Software Foundation, either version 3 of the License. + * + * 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 Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with this program. If not, see <http://www.gnu.org/licenses/>. + */ + +package net.openhft.lang.io.serialization; + +import net.openhft.lang.io.Bytes; +import net.openhft.lang.model.constraints.NotNull; + +import java.io.*; + +/** + * Abstracts serialization implementation, which at least should be able to serialize objects that + * Java built-in serialization is able serialize. In other words, {@code ObjectSerializer} abstracts + * Java serialization re-implementations and extensions. {@link Bytes} is used as core IO interface + * instead of {@link InputStream} + {@link OutputStream} pair, which Java built-in serialization + * use. However, note that {@code Bytes} could always be converted to these old interfaces by + * {@link Bytes#inputStream()} and {@link Bytes#outputStream()}, if needed. + * + * <p>The default fallback implementation is Java built-in serialization itself: + * {@link JDKObjectSerializer}. + * + * <p>Another provided implementation is {@link BytesMarshallableSerializer}, which basically + * extends built-in serialization with some improvements. For example, it could benefit if objects + * implement {@link BytesMarshallable} interface the same way as built-in serialization benefit + * if objects implement {@link Externalizable}. See {@link BytesMarshallableSerializer} docs for + * more information. + * + * <p>This interface is supposed to be implemented to plug such third-party serialization + * re-implementations, as Kryo, fast-serialization, etc. + */ +public interface ObjectSerializer extends Serializable { + /** + * write an object + * + * @param bytes to write to + * @param object object to write + * @param expectedClass which will be provided on read, can be null + */ + void writeSerializable(@NotNull Bytes bytes, Object object, Class expectedClass) throws IOException; + + /** + * Read an object + * + * @param bytes to read + * @param expectedClass proved when writing, can be null + * @param object to populate, can be null + * @return object read. + * @throws IOException if it not possible to serialize the object + * @throws ClassNotFoundException if the expectedClass can not be created + */ + <T> T readSerializable(@NotNull Bytes bytes, Class<T> expectedClass, T object) throws IOException, ClassNotFoundException; +} diff --git a/lang/src/main/java/net/openhft/lang/io/serialization/direct/DirectSerializationFilter.java b/lang/src/main/java/net/openhft/lang/io/serialization/direct/DirectSerializationFilter.java deleted file mode 100644 index c6b5b2e..0000000 --- a/lang/src/main/java/net/openhft/lang/io/serialization/direct/DirectSerializationFilter.java +++ /dev/null @@ -1,29 +0,0 @@ -package net.openhft.lang.io.serialization.direct; - -import java.lang.reflect.Field; -import java.util.*; - -import static net.openhft.lang.io.serialization.direct.FieldMetadata.*; - -public final class DirectSerializationFilter { - public static List<Field> stopAtFirstIneligibleField(List<Field> fields) { - ArrayList<Field> eligibleFields = new ArrayList<Field>(); - for (Field f : fields) { - if (checkEligible(f)) { - eligibleFields.add(f); - } else { - break; - } - } - - return eligibleFields.isEmpty() ? - Collections.<Field>emptyList() : - eligibleFields; - } - - private static boolean checkEligible(Field f) { - return isPrimitive(f) && - !isStatic(f) && - !isTransient(f); - } -}
\ No newline at end of file diff --git a/lang/src/main/java/net/openhft/lang/io/serialization/direct/FieldMetadata.java b/lang/src/main/java/net/openhft/lang/io/serialization/direct/FieldMetadata.java deleted file mode 100644 index d186c77..0000000 --- a/lang/src/main/java/net/openhft/lang/io/serialization/direct/FieldMetadata.java +++ /dev/null @@ -1,22 +0,0 @@ -package net.openhft.lang.io.serialization.direct; - -import java.lang.reflect.*; - -public final class FieldMetadata { - public static boolean isPrimitive(Field f) { - return f.getType().isPrimitive(); - } - - public static boolean isPrimitiveArray(Field f) { - Class<?> clazz = f.getType(); - return clazz.isArray() && clazz.getComponentType().isPrimitive(); - } - - public static boolean isStatic(Field f) { - return Modifier.isStatic(f.getModifiers()); - } - - public static boolean isTransient(Field f) { - return Modifier.isTransient(f.getModifiers()); - } -} diff --git a/lang/src/main/java/net/openhft/lang/io/serialization/impl/AllocateInstanceObjectFactory.java b/lang/src/main/java/net/openhft/lang/io/serialization/impl/AllocateInstanceObjectFactory.java new file mode 100644 index 0000000..70ac6f7 --- /dev/null +++ b/lang/src/main/java/net/openhft/lang/io/serialization/impl/AllocateInstanceObjectFactory.java @@ -0,0 +1,68 @@ +/* + * Copyright (C) 2015 higherfrequencytrading.com + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as published by + * the Free Software Foundation, either version 3 of the License. + * + * 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 Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with this program. If not, see <http://www.gnu.org/licenses/>. + */ + +package net.openhft.lang.io.serialization.impl; + +import net.openhft.lang.io.NativeBytes; +import net.openhft.lang.io.serialization.ObjectFactory; + +import java.lang.reflect.Modifier; + +/** + * Object factory which creates an object by means of {@code Unsafe.allocateInstance()} call, + * i. e. without calling constructor. + * + * @param <E> type of created objects + */ +public final class AllocateInstanceObjectFactory<E> implements ObjectFactory<E> { + private static final long serialVersionUID = 0L; + + private final Class<E> eClass; + + public AllocateInstanceObjectFactory(Class<E> eClass) { + if (eClass.isInterface() || Modifier.isAbstract(eClass.getModifiers()) || + eClass.isEnum()) { + throw new IllegalArgumentException(eClass + " should be a non-abstract non-enum class"); + } + this.eClass = eClass; + } + + public Class<E> allocatedClass() { + return eClass; + } + + @SuppressWarnings("unchecked") + @Override + public E create() throws InstantiationException { + return (E) NativeBytes.UNSAFE.allocateInstance(eClass); + } + + @Override + public boolean equals(Object obj) { + return obj != null && obj.getClass() == getClass() && + ((AllocateInstanceObjectFactory) obj).eClass == eClass; + } + + @Override + public int hashCode() { + return eClass.hashCode(); + } + + @Override + public String toString() { + return getClass().getSimpleName() + "{eClass=" + eClass + "}"; + } +} diff --git a/lang/src/main/java/net/openhft/lang/io/serialization/impl/ByteBufferMarshaller.java b/lang/src/main/java/net/openhft/lang/io/serialization/impl/ByteBufferMarshaller.java new file mode 100644 index 0000000..ac4051f --- /dev/null +++ b/lang/src/main/java/net/openhft/lang/io/serialization/impl/ByteBufferMarshaller.java @@ -0,0 +1,71 @@ +/* + * Copyright (C) 2015 higherfrequencytrading.com + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as published by + * the Free Software Foundation, either version 3 of the License. + * + * 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 Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with this program. If not, see <http://www.gnu.org/licenses/>. + */ + +package net.openhft.lang.io.serialization.impl; + +import net.openhft.lang.io.Bytes; +import net.openhft.lang.io.serialization.CompactBytesMarshaller; +import net.openhft.lang.model.constraints.Nullable; + +import java.nio.ByteBuffer; + +public enum ByteBufferMarshaller implements CompactBytesMarshaller<ByteBuffer> { + INSTANCE; + + @Override + public byte code() { + return BYTE_BUFFER_CODE; + } + + @Override + public void write(Bytes bytes, ByteBuffer byteBuffer) { + int position = byteBuffer.position(); + bytes.writeStopBit(byteBuffer.remaining()); + bytes.write(byteBuffer); + + // reset the position back as we found it + byteBuffer.position(position); + } + + @Override + public ByteBuffer read(Bytes bytes) { + return read(bytes, null); + } + + @Override + public ByteBuffer read(Bytes bytes, @Nullable ByteBuffer byteBuffer) { + long length = bytes.readStopBit(); + assert length <= Integer.MAX_VALUE; + if (length < 0 || length > Integer.MAX_VALUE) { + throw new IllegalStateException("Invalid length: " + length); + } + if (byteBuffer == null || byteBuffer.capacity() < length) { + byteBuffer = newByteBuffer((int) length); + + } else { + byteBuffer.position(0); + byteBuffer.limit((int) length); + } + + bytes.read(byteBuffer); + byteBuffer.flip(); + return byteBuffer; + } + + protected ByteBuffer newByteBuffer(int length) { + return ByteBuffer.allocate(length); + } +} diff --git a/lang/src/main/java/net/openhft/lang/io/serialization/impl/ByteBufferZMarshaller.java b/lang/src/main/java/net/openhft/lang/io/serialization/impl/ByteBufferZMarshaller.java new file mode 100644 index 0000000..fe648e9 --- /dev/null +++ b/lang/src/main/java/net/openhft/lang/io/serialization/impl/ByteBufferZMarshaller.java @@ -0,0 +1,103 @@ +/* + * Copyright (C) 2015 higherfrequencytrading.com + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as published by + * the Free Software Foundation, either version 3 of the License. + * + * 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 Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with this program. If not, see <http://www.gnu.org/licenses/>. + */ + +package net.openhft.lang.io.serialization.impl; + +import net.openhft.lang.io.Bytes; +import net.openhft.lang.io.serialization.CompactBytesMarshaller; +import net.openhft.lang.model.constraints.Nullable; + +import java.io.DataInputStream; +import java.io.DataOutputStream; +import java.io.IOException; +import java.nio.ByteBuffer; +import java.util.zip.DeflaterOutputStream; +import java.util.zip.InflaterInputStream; + +public enum ByteBufferZMarshaller implements CompactBytesMarshaller<ByteBuffer> { + INSTANCE; + + @Override + public byte code() { + return BYTE_BUFFER_CODE; + } + + @Override + public void write(Bytes bytes, ByteBuffer byteBuffer) { + bytes.writeStopBit(byteBuffer.remaining()); + long position = bytes.position(); + bytes.clear(); + bytes.position(position + 4); + DataOutputStream dos = new DataOutputStream(new DeflaterOutputStream(bytes.outputStream())); + try { + while (byteBuffer.remaining() >= 8) + dos.writeLong(byteBuffer.getLong()); + while (byteBuffer.remaining() > 0) + dos.write(byteBuffer.get()); + dos.close(); + } catch (IOException e) { + throw new IllegalStateException(e); + } + bytes.writeUnsignedInt(position, bytes.position() - position - 4); + + bytes.write(byteBuffer); + } + + @Override + public ByteBuffer read(Bytes bytes) { + return read(bytes, null); + } + + @Override + public ByteBuffer read(Bytes bytes, @Nullable ByteBuffer byteBuffer) { + long length = bytes.readStopBit(); + if (length < 0 || length > Integer.MAX_VALUE) { + throw new IllegalStateException("Invalid length: " + length); + } + if (byteBuffer == null || byteBuffer.capacity() < length) { + byteBuffer = newByteBuffer((int) length); + + } else { + byteBuffer.clear(); + } + byteBuffer.limit((int) length); + + long position = bytes.position(); + long end = position + length; + + long limit = bytes.limit(); + bytes.limit(end); + + DataInputStream dis = new DataInputStream(new InflaterInputStream(bytes.inputStream())); + try { + while (byteBuffer.remaining() >= 8) + byteBuffer.putLong(dis.readLong()); + while (byteBuffer.remaining() >= 0) + byteBuffer.put(dis.readByte()); + } catch (IOException e) { + throw new IllegalStateException(e); + } + bytes.position(end); + bytes.limit(limit); + + byteBuffer.flip(); + return byteBuffer; + } + + protected ByteBuffer newByteBuffer(int length) { + return ByteBuffer.allocate(length); + } +} diff --git a/lang/src/main/java/net/openhft/lang/io/serialization/impl/BytesMarshallableMarshaller.java b/lang/src/main/java/net/openhft/lang/io/serialization/impl/BytesMarshallableMarshaller.java index ebcb95f..59ea161 100644 --- a/lang/src/main/java/net/openhft/lang/io/serialization/impl/BytesMarshallableMarshaller.java +++ b/lang/src/main/java/net/openhft/lang/io/serialization/impl/BytesMarshallableMarshaller.java @@ -1,31 +1,34 @@ /* - * Copyright 2013 Peter Lawrey + * Copyright (C) 2015 higherfrequencytrading.com * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as published by + * the Free Software Foundation, either version 3 of the License. * - * http://www.apache.org/licenses/LICENSE-2.0 + * 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 Lesser General Public License for more details. * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. + * You should have received a copy of the GNU Lesser General Public License + * along with this program. If not, see <http://www.gnu.org/licenses/>. */ package net.openhft.lang.io.serialization.impl; import net.openhft.lang.io.Bytes; +import net.openhft.lang.io.NativeBytes; import net.openhft.lang.io.serialization.BytesMarshallable; import net.openhft.lang.io.serialization.BytesMarshaller; -import net.openhft.lang.io.NativeBytes; -import org.jetbrains.annotations.NotNull; +import net.openhft.lang.model.constraints.NotNull; +import net.openhft.lang.model.constraints.Nullable; /** * @author peter.lawrey */ -public class BytesMarshallableMarshaller<E extends BytesMarshallable> implements BytesMarshaller<E> { +public class BytesMarshallableMarshaller<E extends BytesMarshallable> + implements BytesMarshaller<E> { + private static final long serialVersionUID = 0L; @NotNull private final Class<E> classMarshaled; @@ -33,6 +36,10 @@ public class BytesMarshallableMarshaller<E extends BytesMarshallable> implements this.classMarshaled = classMarshaled; } + public final Class<E> marshaledClass() { + return classMarshaled; + } + @Override public void write(@NotNull Bytes bytes, @NotNull E e) { e.writeMarshallable(bytes); @@ -40,13 +47,42 @@ public class BytesMarshallableMarshaller<E extends BytesMarshallable> implements @Override public E read(@NotNull Bytes bytes) { - E e; - try { - e = (E) NativeBytes.UNSAFE.allocateInstance(classMarshaled); - } catch (Exception e2) { - throw new IllegalStateException(e2); + return read(bytes, null); + } + + @Nullable + @Override + public E read(Bytes bytes, @Nullable E e) { + if (e == null) { + try { + e = getInstance(); + } catch (Exception e2) { + throw new IllegalStateException(e2); + } } e.readMarshallable(bytes); return e; } + + @SuppressWarnings("unchecked") + @NotNull + protected E getInstance() throws Exception { + return (E) NativeBytes.UNSAFE.allocateInstance(classMarshaled); + } + + @Override + public boolean equals(Object obj) { + return obj != null && obj.getClass() == getClass() && + ((BytesMarshallableMarshaller) obj).classMarshaled == classMarshaled; + } + + @Override + public int hashCode() { + return classMarshaled.hashCode(); + } + + @Override + public String toString() { + return getClass().getSimpleName() + "{classMarshaled=" + classMarshaled + "}"; + } } diff --git a/lang/src/main/java/net/openhft/lang/io/serialization/impl/ClassMarshaller.java b/lang/src/main/java/net/openhft/lang/io/serialization/impl/ClassMarshaller.java index db36c37..92be9bd 100644 --- a/lang/src/main/java/net/openhft/lang/io/serialization/impl/ClassMarshaller.java +++ b/lang/src/main/java/net/openhft/lang/io/serialization/impl/ClassMarshaller.java @@ -1,17 +1,17 @@ /* - * Copyright 2013 Peter Lawrey + * Copyright (C) 2015 higherfrequencytrading.com * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as published by + * the Free Software Foundation, either version 3 of the License. * - * http://www.apache.org/licenses/LICENSE-2.0 + * 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 Lesser General Public License for more details. * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. + * You should have received a copy of the GNU Lesser General Public License + * along with this program. If not, see <http://www.gnu.org/licenses/>. */ package net.openhft.lang.io.serialization.impl; @@ -19,8 +19,9 @@ package net.openhft.lang.io.serialization.impl; import net.openhft.lang.Compare; import net.openhft.lang.io.Bytes; import net.openhft.lang.io.serialization.CompactBytesMarshaller; -import org.jetbrains.annotations.NotNull; -import org.jetbrains.annotations.Nullable; +import net.openhft.lang.model.constraints.NotNull; +import net.openhft.lang.model.constraints.Nullable; +import net.openhft.lang.pool.StringInterner; import java.lang.ref.WeakReference; import java.math.BigDecimal; @@ -32,7 +33,8 @@ import java.util.Map; /** * @author peter.lawrey */ -public class ClassMarshaller implements CompactBytesMarshaller<Class> { +public class ClassMarshaller extends ImmutableMarshaller<Class> + implements CompactBytesMarshaller<Class> { private static final int CACHE_SIZE = 1019; private static final Map<String, Class> SC_SHORT_NAME = new LinkedHashMap<String, Class>(); private static final Map<Class, String> CS_SHORT_NAME = new LinkedHashMap<Class, String>(); @@ -48,7 +50,7 @@ public class ClassMarshaller implements CompactBytesMarshaller<Class> { } private final ClassLoader classLoader; - private final StringBuilder className = new StringBuilder(40); + private static final StringBuilderPool sbp = new StringBuilderPool(); @Nullable @SuppressWarnings("unchecked") private WeakReference<Class>[] classWeakReference = null; @@ -68,9 +70,9 @@ public class ClassMarshaller implements CompactBytesMarshaller<Class> { @Nullable @Override public Class read(@NotNull Bytes bytes) { - className.setLength(0); - bytes.readUTFΔ(className); - return load(className); + StringBuilder sb = sbp.acquireStringBuilder(); + bytes.readUTFΔ(sb); + return load(sb); } @Nullable @@ -82,15 +84,16 @@ public class ClassMarshaller implements CompactBytesMarshaller<Class> { WeakReference<Class> ref = classWeakReference[hash]; if (ref != null) { Class clazz = ref.get(); - if (clazz != null && clazz.getName().equals(name)) + if (clazz != null && StringInterner.isEqual(clazz.getName(), name)) return clazz; } try { - Class<?> clazz = SC_SHORT_NAME.get(name.toString()); + String className = name.toString(); + Class<?> clazz = SC_SHORT_NAME.get(className); if (clazz != null) return clazz; - clazz = classLoader.loadClass(name.toString()); + clazz = classLoader.loadClass(className); classWeakReference[hash] = new WeakReference<Class>(clazz); return clazz; } catch (ClassNotFoundException e) { @@ -100,6 +103,7 @@ public class ClassMarshaller implements CompactBytesMarshaller<Class> { @Override public byte code() { - return 'C' & 31; // control C + return CLASS_CODE; // control C } } +
\ No newline at end of file diff --git a/lang/src/main/java/net/openhft/lang/io/serialization/impl/CollectionMarshaller.java b/lang/src/main/java/net/openhft/lang/io/serialization/impl/CollectionMarshaller.java new file mode 100644 index 0000000..dbf8f53 --- /dev/null +++ b/lang/src/main/java/net/openhft/lang/io/serialization/impl/CollectionMarshaller.java @@ -0,0 +1,72 @@ +/* + * Copyright (C) 2015 higherfrequencytrading.com + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as published by + * the Free Software Foundation, either version 3 of the License. + * + * 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 Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with this program. If not, see <http://www.gnu.org/licenses/>. + */ + +package net.openhft.lang.io.serialization.impl; + +import net.openhft.lang.io.Bytes; +import net.openhft.lang.io.serialization.BytesMarshaller; +import net.openhft.lang.model.constraints.Nullable; + +import java.util.Collection; + +abstract class CollectionMarshaller<E, C extends Collection<E>> { + + public static final int NULL_LENGTH = -1; + final BytesMarshaller<E> eBytesMarshaller; + + protected CollectionMarshaller(BytesMarshaller<E> eBytesMarshaller) { + this.eBytesMarshaller = eBytesMarshaller; + } + + public void write(Bytes bytes, C c) { + if (c == null) { + bytes.writeStopBit(NULL_LENGTH); + return; + } + bytes.writeStopBit(c.size()); + for (E e : c) { + eBytesMarshaller.write(bytes, e); + } + } + + public C read(Bytes bytes) { + return read(bytes, null); + } + + abstract C newCollection(); + + public C read(Bytes bytes, @Nullable C c) { + long length = bytes.readStopBit(); + + if (length == 0 && c != null) { + c.clear(); + return c; + } + + if (length < NULL_LENGTH || length > Integer.MAX_VALUE) + throw new IllegalStateException("Invalid length: " + length); + + if (length == NULL_LENGTH) + return null; + + if (c == null) + c = newCollection(); + + return readCollection(bytes, c, (int) length); + } + + abstract C readCollection(Bytes bytes, C c, int length); +} diff --git a/lang/src/main/java/net/openhft/lang/io/serialization/impl/CompactEnumBytesMarshaller.java b/lang/src/main/java/net/openhft/lang/io/serialization/impl/CompactEnumBytesMarshaller.java index 08f66e4..676c86c 100644..100755 --- a/lang/src/main/java/net/openhft/lang/io/serialization/impl/CompactEnumBytesMarshaller.java +++ b/lang/src/main/java/net/openhft/lang/io/serialization/impl/CompactEnumBytesMarshaller.java @@ -1,26 +1,26 @@ /* - * Copyright 2013 Peter Lawrey + * Copyright (C) 2015 higherfrequencytrading.com * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as published by + * the Free Software Foundation, either version 3 of the License. * - * http://www.apache.org/licenses/LICENSE-2.0 + * 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 Lesser General Public License for more details. * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. + * You should have received a copy of the GNU Lesser General Public License + * along with this program. If not, see <http://www.gnu.org/licenses/>. */ package net.openhft.lang.io.serialization.impl; import net.openhft.lang.io.serialization.CompactBytesMarshaller; -import org.jetbrains.annotations.NotNull; +import net.openhft.lang.model.constraints.NotNull; /** - * Created with IntelliJ IDEA. User: peter Date: 09/12/13 Time: 17:05 To change this template use File | Settings | File + * Created with IntelliJ IDEA. User: peter.lawrey Date: 09/12/13 Time: 17:05 * Templates. */ public class CompactEnumBytesMarshaller<E> extends GenericEnumMarshaller<E> implements CompactBytesMarshaller<E> { diff --git a/lang/src/main/java/net/openhft/lang/io/serialization/impl/DateMarshaller.java b/lang/src/main/java/net/openhft/lang/io/serialization/impl/DateMarshaller.java index 03f6bb1..1e949c5 100644 --- a/lang/src/main/java/net/openhft/lang/io/serialization/impl/DateMarshaller.java +++ b/lang/src/main/java/net/openhft/lang/io/serialization/impl/DateMarshaller.java @@ -1,25 +1,25 @@ /* - * Copyright 2013 Peter Lawrey + * Copyright (C) 2015 higherfrequencytrading.com * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as published by + * the Free Software Foundation, either version 3 of the License. * - * http://www.apache.org/licenses/LICENSE-2.0 + * 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 Lesser General Public License for more details. * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. + * You should have received a copy of the GNU Lesser General Public License + * along with this program. If not, see <http://www.gnu.org/licenses/>. */ package net.openhft.lang.io.serialization.impl; import net.openhft.lang.io.Bytes; import net.openhft.lang.io.serialization.CompactBytesMarshaller; -import org.jetbrains.annotations.NotNull; -import org.jetbrains.annotations.Nullable; +import net.openhft.lang.model.constraints.NotNull; +import net.openhft.lang.model.constraints.Nullable; import java.util.Date; @@ -27,8 +27,8 @@ import java.util.Date; * @author peter.lawrey */ public class DateMarshaller implements CompactBytesMarshaller<Date> { - final int size1; - private final StringBuilder sb = new StringBuilder(); + private final int size1; + private static final StringBuilderPool sbp = new StringBuilderPool(); @Nullable private Date[] interner = null; @@ -65,12 +65,25 @@ public class DateMarshaller implements CompactBytesMarshaller<Date> { @Nullable @Override public Date read(@NotNull Bytes bytes) { + StringBuilder sb = sbp.acquireStringBuilder(); bytes.readUTFΔ(sb); long time = parseLong(sb); return lookupDate(time); } @Nullable + @Override + public Date read(Bytes bytes, @Nullable Date date) { + if (date == null) + return read(bytes); + StringBuilder sb = sbp.acquireStringBuilder(); + bytes.readUTFΔ(sb); + long time = parseLong(sb); + date.setTime(time); + return date; + } + + @Nullable private Date lookupDate(long time) { int idx = hashFor(time); if (interner == null) @@ -90,6 +103,6 @@ public class DateMarshaller implements CompactBytesMarshaller<Date> { @Override public byte code() { - return 'T' & 31; // Control T. + return DATE_CODE; // Control T. } } diff --git a/lang/src/main/java/net/openhft/lang/io/serialization/impl/EnumBytesMarshaller.java b/lang/src/main/java/net/openhft/lang/io/serialization/impl/EnumBytesMarshaller.java index 11f060b..189f7f5 100644 --- a/lang/src/main/java/net/openhft/lang/io/serialization/impl/EnumBytesMarshaller.java +++ b/lang/src/main/java/net/openhft/lang/io/serialization/impl/EnumBytesMarshaller.java @@ -1,25 +1,25 @@ /* - * Copyright 2013 Peter Lawrey + * Copyright (C) 2015 higherfrequencytrading.com * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as published by + * the Free Software Foundation, either version 3 of the License. * - * http://www.apache.org/licenses/LICENSE-2.0 + * 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 Lesser General Public License for more details. * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. + * You should have received a copy of the GNU Lesser General Public License + * along with this program. If not, see <http://www.gnu.org/licenses/>. */ package net.openhft.lang.io.serialization.impl; import net.openhft.lang.io.Bytes; import net.openhft.lang.io.serialization.BytesMarshaller; -import org.jetbrains.annotations.NotNull; -import org.jetbrains.annotations.Nullable; +import net.openhft.lang.model.constraints.NotNull; +import net.openhft.lang.model.constraints.Nullable; import java.util.BitSet; import java.util.LinkedHashMap; @@ -28,19 +28,17 @@ import java.util.Map; /** * @author peter.lawrey */ -public class EnumBytesMarshaller<E extends Enum<E>> implements BytesMarshaller<E> { - @NotNull - private final Class<E> classMarshaled; +public class EnumBytesMarshaller<E extends Enum<E>> extends ImmutableMarshaller<E> + implements BytesMarshaller<E> { @SuppressWarnings("unchecked") private final E[] interner = (E[]) new Enum[1024]; private final BitSet internerDup = new BitSet(1024); - private final Map<String, E> map = new LinkedHashMap<String, E>(); + private final Map<String, E> map = new LinkedHashMap<String, E>(64); private final E defaultValue; private final int mask; - private final StringBuilder reader = new StringBuilder(); + private static final StringBuilderPool sbp = new StringBuilderPool(); public EnumBytesMarshaller(@NotNull Class<E> classMarshaled, E defaultValue) { - this.classMarshaled = classMarshaled; this.defaultValue = defaultValue; mask = interner.length - 1; @@ -52,6 +50,7 @@ public class EnumBytesMarshaller<E extends Enum<E>> implements BytesMarshaller<E //noinspection UnqualifiedFieldAccess,AssignmentToNull interner[idx] = null; internerDup.set(idx); + } else { interner[idx] = e; } @@ -77,17 +76,18 @@ public class EnumBytesMarshaller<E extends Enum<E>> implements BytesMarshaller<E @Override public E read(@NotNull Bytes bytes) { - bytes.readUTFΔ(reader); - return builderToEnum(); + StringBuilder sb = sbp.acquireStringBuilder(); + bytes.readUTFΔ(sb); + return builderToEnum(sb); } - private E builderToEnum() { - int num = hashFor(reader); + private E builderToEnum(StringBuilder sb) { + int num = hashFor(sb); int idx = num & mask; E e = interner[idx]; if (e != null) return e; if (!internerDup.get(idx)) return defaultValue; - e = map.get(reader.toString()); + e = map.get(sb.toString()); return e == null ? defaultValue : e; } } diff --git a/lang/src/main/java/net/openhft/lang/io/serialization/impl/ExternalizableMarshaller.java b/lang/src/main/java/net/openhft/lang/io/serialization/impl/ExternalizableMarshaller.java index 05f5390..4e9b82c 100644 --- a/lang/src/main/java/net/openhft/lang/io/serialization/impl/ExternalizableMarshaller.java +++ b/lang/src/main/java/net/openhft/lang/io/serialization/impl/ExternalizableMarshaller.java @@ -1,25 +1,26 @@ /* - * Copyright 2013 Peter Lawrey + * Copyright (C) 2015 higherfrequencytrading.com * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as published by + * the Free Software Foundation, either version 3 of the License. * - * http://www.apache.org/licenses/LICENSE-2.0 + * 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 Lesser General Public License for more details. * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. + * You should have received a copy of the GNU Lesser General Public License + * along with this program. If not, see <http://www.gnu.org/licenses/>. */ package net.openhft.lang.io.serialization.impl; import net.openhft.lang.io.Bytes; -import net.openhft.lang.io.serialization.BytesMarshaller; import net.openhft.lang.io.NativeBytes; -import org.jetbrains.annotations.NotNull; +import net.openhft.lang.io.serialization.BytesMarshaller; +import net.openhft.lang.model.constraints.NotNull; +import net.openhft.lang.model.constraints.Nullable; import java.io.Externalizable; import java.io.IOException; @@ -28,6 +29,8 @@ import java.io.IOException; * @author peter.lawrey */ public class ExternalizableMarshaller<E extends Externalizable> implements BytesMarshaller<E> { + private static final long serialVersionUID = 0L; + @NotNull private final Class<E> classMarshaled; @@ -35,6 +38,10 @@ public class ExternalizableMarshaller<E extends Externalizable> implements Bytes this.classMarshaled = classMarshaled; } + public final Class<E> marshaledClass() { + return classMarshaled; + } + @Override public void write(Bytes bytes, @NotNull E e) { try { @@ -46,13 +53,41 @@ public class ExternalizableMarshaller<E extends Externalizable> implements Bytes @Override public E read(Bytes bytes) { - E e; + return read(bytes, null); + } + + @Nullable + @Override + public E read(Bytes bytes, @Nullable E e) { try { - e = (E) NativeBytes.UNSAFE.allocateInstance(classMarshaled); + if (e == null) + e = getInstance(); e.readExternal(bytes); + return e; } catch (Exception e2) { throw new IllegalStateException(e2); } - return e; + } + + @SuppressWarnings("unchecked") + @NotNull + protected E getInstance() throws Exception { + return (E) NativeBytes.UNSAFE.allocateInstance(classMarshaled); + } + + @Override + public boolean equals(Object obj) { + return obj != null && obj.getClass() == getClass() && + ((ExternalizableMarshaller) obj).classMarshaled == classMarshaled; + } + + @Override + public int hashCode() { + return classMarshaled.hashCode(); + } + + @Override + public String toString() { + return getClass().getSimpleName() + "{marshaledClass=" + classMarshaled + "}"; } } diff --git a/lang/src/main/java/net/openhft/lang/io/serialization/impl/GenericEnumMarshaller.java b/lang/src/main/java/net/openhft/lang/io/serialization/impl/GenericEnumMarshaller.java index 8a1b52d..8ed4590 100644 --- a/lang/src/main/java/net/openhft/lang/io/serialization/impl/GenericEnumMarshaller.java +++ b/lang/src/main/java/net/openhft/lang/io/serialization/impl/GenericEnumMarshaller.java @@ -1,25 +1,25 @@ /* - * Copyright 2013 Peter Lawrey + * Copyright (C) 2015 higherfrequencytrading.com * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as published by + * the Free Software Foundation, either version 3 of the License. * - * http://www.apache.org/licenses/LICENSE-2.0 + * 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 Lesser General Public License for more details. * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. + * You should have received a copy of the GNU Lesser General Public License + * along with this program. If not, see <http://www.gnu.org/licenses/>. */ package net.openhft.lang.io.serialization.impl; import net.openhft.lang.io.Bytes; import net.openhft.lang.io.serialization.BytesMarshaller; -import org.jetbrains.annotations.NotNull; -import org.jetbrains.annotations.Nullable; +import net.openhft.lang.model.constraints.NotNull; +import net.openhft.lang.model.constraints.Nullable; import java.lang.reflect.Constructor; import java.lang.reflect.Method; @@ -30,17 +30,20 @@ import java.util.Map; * @author peter.lawrey */ public class GenericEnumMarshaller<E> implements BytesMarshaller<E> { - @NotNull - private final Class<E> classMarshaled; + private final int capacity; @Nullable - private final Constructor<E> constructor; + private transient final Constructor<E> constructor; @Nullable - private final Method valueOf; + private transient final Method valueOf; @NotNull private final Map<String, E> map; + //used by the read resolve method + private final Class<E> classMarshaled; + public GenericEnumMarshaller(@NotNull Class<E> classMarshaled, final int capacity) { this.classMarshaled = classMarshaled; + this.capacity = capacity; Constructor<E> constructor = null; Method valueOf = null; try { @@ -48,6 +51,7 @@ public class GenericEnumMarshaller<E> implements BytesMarshaller<E> { } catch (NoSuchMethodException e) { try { constructor = classMarshaled.getConstructor(String.class); + constructor.setAccessible(true); } catch (NoSuchMethodException e1) { throw new IllegalArgumentException(classMarshaled + " doesn't have a valueOf(String) or a Constructor(String)"); } @@ -62,6 +66,10 @@ public class GenericEnumMarshaller<E> implements BytesMarshaller<E> { }; } + private Object readResolve() { + return new GenericEnumMarshaller(classMarshaled, capacity); + } + @Override public void write(@NotNull Bytes bytes, @Nullable E e) { bytes.writeUTFΔ(e == null ? null : e.toString()); @@ -74,19 +82,26 @@ public class GenericEnumMarshaller<E> implements BytesMarshaller<E> { return s == null ? null : valueOf(s); } + @Nullable + @Override + public E read(Bytes bytes, @Nullable E e) { + return read(bytes); + } + private E valueOf(String s) { E e = map.get(s); if (e == null) try { if (constructor != null) { map.put(s, e = constructor.newInstance(s)); + } else { @SuppressWarnings("unchecked") E invoke = (E) valueOf.invoke(null, s); map.put(s, e = invoke); } } catch (Exception t) { - throw new AssertionError(t.getCause()); + throw new AssertionError(t); } return e; } diff --git a/lang/src/main/java/net/openhft/lang/io/serialization/impl/ImmutableMarshaller.java b/lang/src/main/java/net/openhft/lang/io/serialization/impl/ImmutableMarshaller.java new file mode 100644 index 0000000..de55a9c --- /dev/null +++ b/lang/src/main/java/net/openhft/lang/io/serialization/impl/ImmutableMarshaller.java @@ -0,0 +1,29 @@ +/* + * Copyright (C) 2015 higherfrequencytrading.com + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as published by + * the Free Software Foundation, either version 3 of the License. + * + * 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 Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with this program. If not, see <http://www.gnu.org/licenses/>. + */ + +package net.openhft.lang.io.serialization.impl; + +import net.openhft.lang.io.Bytes; +import net.openhft.lang.io.serialization.BytesMarshaller; +import net.openhft.lang.model.constraints.Nullable; + +abstract class ImmutableMarshaller<E> implements BytesMarshaller<E> { + @Nullable + @Override + public final E read(Bytes bytes, @Nullable E e) { + return read(bytes); + } +} diff --git a/lang/src/main/java/net/openhft/lang/io/serialization/impl/ListMarshaller.java b/lang/src/main/java/net/openhft/lang/io/serialization/impl/ListMarshaller.java new file mode 100644 index 0000000..cc9b592 --- /dev/null +++ b/lang/src/main/java/net/openhft/lang/io/serialization/impl/ListMarshaller.java @@ -0,0 +1,57 @@ +/* + * Copyright (C) 2015 higherfrequencytrading.com + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as published by + * the Free Software Foundation, either version 3 of the License. + * + * 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 Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with this program. If not, see <http://www.gnu.org/licenses/>. + */ + +package net.openhft.lang.io.serialization.impl; + +import net.openhft.lang.io.Bytes; +import net.openhft.lang.io.serialization.BytesMarshaller; +import net.openhft.lang.io.serialization.CompactBytesMarshaller; + +import java.util.ArrayList; +import java.util.List; + +public class ListMarshaller<E> extends CollectionMarshaller<E, List<E>> implements CompactBytesMarshaller<List<E>> { + + ListMarshaller(BytesMarshaller<E> eBytesMarshaller) { + super(eBytesMarshaller); + } + + public static <E> BytesMarshaller<List<E>> of(BytesMarshaller<E> eBytesMarshaller) { + return new ListMarshaller<E>(eBytesMarshaller); + } + + @Override + public byte code() { + return LIST_CODE; + } + + @Override + List<E> newCollection() { + return new ArrayList<E>(); + } + + @Override + List<E> readCollection(Bytes bytes, List<E> es, int length) { + List<E> ret = es; + ret.clear(); + + for (int i = 0; i < length; i++) { + ret.add(eBytesMarshaller.read(bytes)); + } + + return ret; + } +} diff --git a/lang/src/main/java/net/openhft/lang/io/serialization/impl/MapMarshaller.java b/lang/src/main/java/net/openhft/lang/io/serialization/impl/MapMarshaller.java new file mode 100755 index 0000000..195d96d --- /dev/null +++ b/lang/src/main/java/net/openhft/lang/io/serialization/impl/MapMarshaller.java @@ -0,0 +1,75 @@ +/* + * Copyright (C) 2015 higherfrequencytrading.com + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as published by + * the Free Software Foundation, either version 3 of the License. + * + * 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 Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with this program. If not, see <http://www.gnu.org/licenses/>. + */ + +package net.openhft.lang.io.serialization.impl; + +import net.openhft.lang.io.Bytes; +import net.openhft.lang.io.serialization.BytesMarshaller; +import net.openhft.lang.io.serialization.CompactBytesMarshaller; +import net.openhft.lang.model.constraints.Nullable; + +import java.util.LinkedHashMap; +import java.util.Map; + +/** + * Created by peter.lawrey on 24/10/14. + */ +public class MapMarshaller<K, V> implements CompactBytesMarshaller<Map<K, V>> { + private final BytesMarshaller<K> kBytesMarshaller; + private final BytesMarshaller<V> vBytesMarshaller; + + MapMarshaller(BytesMarshaller<K> kBytesMarshaller, BytesMarshaller<V> vBytesMarshaller) { + this.kBytesMarshaller = kBytesMarshaller; + this.vBytesMarshaller = vBytesMarshaller; + } + + @Override + public byte code() { + return MAP_CODE; + } + + @Override + public void write(Bytes bytes, Map<K, V> kvMap) { + bytes.writeInt(kvMap.size()); + for (Map.Entry<K, V> entry : kvMap.entrySet()) { + kBytesMarshaller.write(bytes, entry.getKey()); + vBytesMarshaller.write(bytes, entry.getValue()); + } + } + + @Override + public Map<K, V> read(Bytes bytes) { + return read(bytes, null); + } + + @Override + public Map<K, V> read(Bytes bytes, @Nullable Map<K, V> kvMap) { + if (kvMap == null) { + kvMap = new LinkedHashMap<K, V>(); + + } else { + kvMap.clear(); + } + int size = bytes.readInt(); + for (int i = 0; i < size; i++) + kvMap.put(kBytesMarshaller.read(bytes), vBytesMarshaller.read(bytes)); + return kvMap; + } + + public static <K, V> BytesMarshaller<Map<K, V>> of(BytesMarshaller<K> keyMarshaller, BytesMarshaller<V> valueMarshaller) { + return new MapMarshaller<K, V>(keyMarshaller, valueMarshaller); + } +} diff --git a/lang/src/main/java/net/openhft/lang/io/serialization/impl/NewInstanceObjectFactory.java b/lang/src/main/java/net/openhft/lang/io/serialization/impl/NewInstanceObjectFactory.java new file mode 100644 index 0000000..0fba720 --- /dev/null +++ b/lang/src/main/java/net/openhft/lang/io/serialization/impl/NewInstanceObjectFactory.java @@ -0,0 +1,62 @@ +/* + * Copyright (C) 2015 higherfrequencytrading.com + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as published by + * the Free Software Foundation, either version 3 of the License. + * + * 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 Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with this program. If not, see <http://www.gnu.org/licenses/>. + */ + +package net.openhft.lang.io.serialization.impl; + +import net.openhft.lang.io.serialization.ObjectFactory; + +import java.lang.reflect.Modifier; + +/** + * Object factory which creates an object by means of {@link Class#newInstance()} call, + * i. e. with calling the default no-arg constructor of the class. + * + * @param <E> type of created objects + */ +public final class NewInstanceObjectFactory<E> implements ObjectFactory<E> { + private static final long serialVersionUID = 0L; + + private final Class<E> eClass; + + public NewInstanceObjectFactory(Class<E> eClass) { + if (eClass.isInterface() || Modifier.isAbstract(eClass.getModifiers()) || + eClass.isEnum()) { + throw new IllegalArgumentException(eClass + " should be a non-abstract non-enum class"); + } + this.eClass = eClass; + } + + @Override + public E create() throws IllegalAccessException, InstantiationException { + return eClass.newInstance(); + } + + @Override + public boolean equals(Object obj) { + return obj != null && obj.getClass() == getClass() && + ((NewInstanceObjectFactory) obj).eClass == eClass; + } + + @Override + public int hashCode() { + return eClass.hashCode(); + } + + @Override + public String toString() { + return getClass().getSimpleName() + "{eClass=" + eClass + "}"; + } +} diff --git a/lang/src/main/java/net/openhft/lang/io/serialization/impl/NoMarshaller.java b/lang/src/main/java/net/openhft/lang/io/serialization/impl/NoMarshaller.java index 1369c4a..c76054c 100644 --- a/lang/src/main/java/net/openhft/lang/io/serialization/impl/NoMarshaller.java +++ b/lang/src/main/java/net/openhft/lang/io/serialization/impl/NoMarshaller.java @@ -1,23 +1,24 @@ /* - * Copyright 2013 Peter Lawrey + * Copyright (C) 2015 higherfrequencytrading.com * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as published by + * the Free Software Foundation, either version 3 of the License. * - * http://www.apache.org/licenses/LICENSE-2.0 + * 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 Lesser General Public License for more details. * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. + * You should have received a copy of the GNU Lesser General Public License + * along with this program. If not, see <http://www.gnu.org/licenses/>. */ package net.openhft.lang.io.serialization.impl; import net.openhft.lang.io.Bytes; import net.openhft.lang.io.serialization.BytesMarshaller; +import net.openhft.lang.model.constraints.Nullable; /** * Created with IntelliJ IDEA. User: peter.lawrey Date: 19/09/13 Time: 18:26 To change this template use File | Settings | File @@ -35,4 +36,10 @@ public enum NoMarshaller implements BytesMarshaller<Void> { public Void read(Bytes bytes) { throw new UnsupportedOperationException(); } + + @Nullable + @Override + public Void read(Bytes bytes, @Nullable Void aVoid) { + throw new UnsupportedOperationException(); + } } diff --git a/lang/src/main/java/net/openhft/lang/io/serialization/impl/NoObjectFactory.java b/lang/src/main/java/net/openhft/lang/io/serialization/impl/NoObjectFactory.java new file mode 100644 index 0000000..c3e061f --- /dev/null +++ b/lang/src/main/java/net/openhft/lang/io/serialization/impl/NoObjectFactory.java @@ -0,0 +1,37 @@ +/* + * Copyright (C) 2015 higherfrequencytrading.com + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as published by + * the Free Software Foundation, either version 3 of the License. + * + * 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 Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with this program. If not, see <http://www.gnu.org/licenses/>. + */ + +package net.openhft.lang.io.serialization.impl; + +import net.openhft.lang.io.serialization.ObjectFactory; + +/** + * Placeholder object factory which always throws {@code UnsupportedOperationException}. + */ +public enum NoObjectFactory implements ObjectFactory { + INSTANCE; + + /** + * Always throws {@code UnsupportedOperationException}. + * + * @return nothing + * @throws UnsupportedOperationException always + */ + @Override + public Object create() throws UnsupportedOperationException { + throw new UnsupportedOperationException(); + } +} diff --git a/lang/src/main/java/net/openhft/lang/io/serialization/impl/NullObjectFactory.java b/lang/src/main/java/net/openhft/lang/io/serialization/impl/NullObjectFactory.java new file mode 100644 index 0000000..af76bc7 --- /dev/null +++ b/lang/src/main/java/net/openhft/lang/io/serialization/impl/NullObjectFactory.java @@ -0,0 +1,40 @@ +/* + * Copyright (C) 2015 higherfrequencytrading.com + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as published by + * the Free Software Foundation, either version 3 of the License. + * + * 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 Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with this program. If not, see <http://www.gnu.org/licenses/>. + */ + +package net.openhft.lang.io.serialization.impl; + +import net.openhft.lang.io.serialization.ObjectFactory; + +/** + * Object factory which always returns {@code null}. + */ +public enum NullObjectFactory implements ObjectFactory { + INSTANCE; + + public static <E> ObjectFactory<E> of() { + return INSTANCE; + } + + /** + * Always returns {@code null}. + * + * @return {@code null} + */ + @Override + public Object create() { + return null; + } +} diff --git a/lang/src/main/java/net/openhft/lang/io/serialization/impl/SetMarshaller.java b/lang/src/main/java/net/openhft/lang/io/serialization/impl/SetMarshaller.java new file mode 100644 index 0000000..e401722 --- /dev/null +++ b/lang/src/main/java/net/openhft/lang/io/serialization/impl/SetMarshaller.java @@ -0,0 +1,55 @@ +/* + * Copyright (C) 2015 higherfrequencytrading.com + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as published by + * the Free Software Foundation, either version 3 of the License. + * + * 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 Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with this program. If not, see <http://www.gnu.org/licenses/>. + */ + +package net.openhft.lang.io.serialization.impl; + +import net.openhft.lang.io.Bytes; +import net.openhft.lang.io.serialization.BytesMarshaller; +import net.openhft.lang.io.serialization.CompactBytesMarshaller; + +import java.util.LinkedHashSet; +import java.util.Set; + +public class SetMarshaller<E> extends CollectionMarshaller<E, Set<E>> implements CompactBytesMarshaller<Set<E>> { + SetMarshaller(BytesMarshaller<E> eBytesMarshaller) { + super(eBytesMarshaller); + } + + public static <E> BytesMarshaller<Set<E>> of(BytesMarshaller<E> eBytesMarshaller) { + return new SetMarshaller<E>(eBytesMarshaller); + } + + @Override + public byte code() { + return SET_CODE; + } + + @Override + Set<E> newCollection() { + return new LinkedHashSet<E>(); + } + + @Override + Set<E> readCollection(Bytes bytes, Set<E> es, int length) { + es.clear(); + + for (int i = 0; i < length; i++) { + es.add(eBytesMarshaller.read(bytes)); + } + + return es; + } +} diff --git a/lang/src/main/java/net/openhft/lang/io/serialization/impl/SnappyStringMarshaller.java b/lang/src/main/java/net/openhft/lang/io/serialization/impl/SnappyStringMarshaller.java new file mode 100755 index 0000000..14ff64f --- /dev/null +++ b/lang/src/main/java/net/openhft/lang/io/serialization/impl/SnappyStringMarshaller.java @@ -0,0 +1,208 @@ +/* + * Copyright (C) 2015 higherfrequencytrading.com + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as published by + * the Free Software Foundation, either version 3 of the License. + * + * 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 Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with this program. If not, see <http://www.gnu.org/licenses/>. + */ + +package net.openhft.lang.io.serialization.impl; + +import net.openhft.lang.io.ByteBufferBytes; +import net.openhft.lang.io.Bytes; +import net.openhft.lang.io.serialization.CompactBytesMarshaller; +import net.openhft.lang.model.constraints.Nullable; +import org.xerial.snappy.Snappy; + +import java.io.IOException; +import java.lang.reflect.Constructor; +import java.lang.reflect.InvocationTargetException; +import java.nio.ByteBuffer; + +/** + * Created by peter.lawrey on 24/10/14. + */ +public enum SnappyStringMarshaller implements CompactBytesMarshaller<CharSequence> { + INSTANCE; + private static final StringFactory STRING_FACTORY = getStringFactory(); + + private static final int NULL_LENGTH = -1; + + private static StringFactory getStringFactory() { + try { + return new StringFactory17(); + } catch (Exception e) { + // do nothing + } + + try { + return new StringFactory16(); + } catch (Exception e) { + // no more alternatives + throw new AssertionError(e); + } + } + + private static final ThreadLocal<ThreadLocals> THREAD_LOCALS = new ThreadLocal<ThreadLocals>(); + + static class ThreadLocals { + ByteBuffer decompressedByteBuffer = ByteBuffer.allocateDirect(32 * 1024); + Bytes decompressedBytes = ByteBufferBytes.wrap(decompressedByteBuffer); + ByteBuffer compressedByteBuffer = ByteBuffer.allocateDirect(0); + + public void clear() { + decompressedByteBuffer.clear(); + decompressedBytes.clear(); + compressedByteBuffer.clear(); + } + } + + @Override + public byte code() { + return STRINGZ_CODE; + } + + public ThreadLocals acquireThreadLocals() { + ThreadLocals threadLocals = THREAD_LOCALS.get(); + if (threadLocals == null) + THREAD_LOCALS.set(threadLocals = new ThreadLocals()); + threadLocals.clear(); + return threadLocals; + } + + @Override + public void write(Bytes bytes, CharSequence s) { + if (s == null) { + bytes.writeStopBit(NULL_LENGTH); + return; + + } else if (s.length() == 0) { + bytes.writeStopBit(0); + return; + } + // write the total length. + int length = s.length(); + bytes.writeStopBit(length); + + ThreadLocals threadLocals = acquireThreadLocals(); + // stream the portions of the string. + Bytes db = threadLocals.decompressedBytes; + ByteBuffer dbb = threadLocals.decompressedByteBuffer; + ByteBuffer cbb = bytes.sliceAsByteBuffer(threadLocals.compressedByteBuffer); + + int position = 0; + while (position < length) { + // 3 is the longest encoding. + while (position < length && db.remaining() >= 3) + db.writeStopBit(s.charAt(position++)); + dbb.position(0); + dbb.limit((int) db.position()); + // portion copied now compress it. + int portionLengthPos = cbb.position(); + cbb.putShort((short) 0); + int compressedLength; + try { + Snappy.compress(dbb, cbb); + compressedLength = cbb.remaining(); + if (compressedLength >= 1 << 16) + throw new AssertionError(); + // unflip. + cbb.position(cbb.limit()); + cbb.limit(cbb.capacity()); + } catch (IOException e) { + throw new AssertionError(e); + } + cbb.putShort(portionLengthPos, (short) compressedLength); + db.clear(); + } + // the end. + cbb.putShort((short) 0); + bytes.position(bytes.position() + cbb.position()); + } + + @Override + public String read(Bytes bytes) { + return read(bytes, null); + } + + @Override + public String read(Bytes bytes, @Nullable CharSequence ignored) { + long size = bytes.readStopBit(); + if (size == NULL_LENGTH) + return null; + if (size < 0 || size > Integer.MAX_VALUE) + throw new IllegalStateException("Invalid length: " + size); + if (size == 0) + return ""; + ThreadLocals threadLocals = acquireThreadLocals(); + // stream the portions of the string. + Bytes db = threadLocals.decompressedBytes; + ByteBuffer dbb = threadLocals.decompressedByteBuffer; + ByteBuffer cbb = bytes.sliceAsByteBuffer(threadLocals.compressedByteBuffer); + + char[] chars = new char[(int) size]; + int pos = 0; + for (int chunkLen; (chunkLen = cbb.getShort() & 0xFFFF) > 0; ) { + cbb.limit(cbb.position() + chunkLen); + dbb.clear(); + try { + Snappy.uncompress(cbb, dbb); + cbb.position(cbb.limit()); + cbb.limit(cbb.capacity()); + } catch (IOException e) { + throw new AssertionError(e); + } + db.position(0); + db.limit(dbb.limit()); + while (db.remaining() > 0) + chars[pos++] = (char) db.readStopBit(); + } + bytes.position(bytes.position() + cbb.position()); + try { + return STRING_FACTORY.fromChars(chars); + } catch (Exception e) { + throw new AssertionError(e); + } + } + + private static abstract class StringFactory { + abstract String fromChars(char[] chars) throws IllegalAccessException, InvocationTargetException, InstantiationException; + } + + private static final class StringFactory16 extends StringFactory { + private final Constructor<String> constructor; + + private StringFactory16() throws NoSuchMethodException { + constructor = String.class.getDeclaredConstructor(int.class, + int.class, char[].class); + constructor.setAccessible(true); + } + + @Override + String fromChars(char[] chars) throws IllegalAccessException, InvocationTargetException, InstantiationException { + return constructor.newInstance(0, chars.length, chars); + } + } + + private static final class StringFactory17 extends StringFactory { + private final Constructor<String> constructor; + + private StringFactory17() throws NoSuchMethodException { + constructor = String.class.getDeclaredConstructor(char[].class, boolean.class); + constructor.setAccessible(true); + } + + @Override + String fromChars(char[] chars) throws IllegalAccessException, InvocationTargetException, InstantiationException { + return constructor.newInstance(chars, true); + } + } +} diff --git a/lang/src/main/java/net/openhft/lang/io/serialization/impl/StringBuilderPool.java b/lang/src/main/java/net/openhft/lang/io/serialization/impl/StringBuilderPool.java new file mode 100755 index 0000000..d7ca469 --- /dev/null +++ b/lang/src/main/java/net/openhft/lang/io/serialization/impl/StringBuilderPool.java @@ -0,0 +1,33 @@ +/* + * Copyright (C) 2015 higherfrequencytrading.com + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as published by + * the Free Software Foundation, either version 3 of the License. + * + * 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 Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with this program. If not, see <http://www.gnu.org/licenses/>. + */ + +package net.openhft.lang.io.serialization.impl; + +/** + * Created by peter.lawrey on 29/10/14. + */ +public class StringBuilderPool { + final ThreadLocal<StringBuilder> sbtl = new ThreadLocal<StringBuilder>(); + + public StringBuilder acquireStringBuilder() { + StringBuilder sb = sbtl.get(); + if (sb == null) { + sbtl.set(sb = new StringBuilder(128)); + } + sb.setLength(0); + return sb; + } +} diff --git a/lang/src/main/java/net/openhft/lang/io/serialization/impl/StringMarshaller.java b/lang/src/main/java/net/openhft/lang/io/serialization/impl/StringMarshaller.java index 2fb2ee5..67843ae 100644 --- a/lang/src/main/java/net/openhft/lang/io/serialization/impl/StringMarshaller.java +++ b/lang/src/main/java/net/openhft/lang/io/serialization/impl/StringMarshaller.java @@ -1,34 +1,35 @@ /* - * Copyright 2013 Peter Lawrey + * Copyright (C) 2015 higherfrequencytrading.com * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as published by + * the Free Software Foundation, either version 3 of the License. * - * http://www.apache.org/licenses/LICENSE-2.0 + * 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 Lesser General Public License for more details. * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. + * You should have received a copy of the GNU Lesser General Public License + * along with this program. If not, see <http://www.gnu.org/licenses/>. */ package net.openhft.lang.io.serialization.impl; import net.openhft.lang.io.Bytes; import net.openhft.lang.io.serialization.CompactBytesMarshaller; +import net.openhft.lang.model.constraints.NotNull; +import net.openhft.lang.model.constraints.Nullable; import net.openhft.lang.pool.StringInterner; -import org.jetbrains.annotations.NotNull; -import org.jetbrains.annotations.Nullable; /** * @author peter.lawrey */ -public class StringMarshaller implements CompactBytesMarshaller<String> { +public class StringMarshaller extends ImmutableMarshaller<String> + implements CompactBytesMarshaller<String> { private final int size; - private final StringBuilder reader = new StringBuilder(); - private StringInterner interner; + private static final StringBuilderPool sbp = new StringBuilderPool(); + private transient StringInterner interner; public StringMarshaller(int size) { this.size = size; @@ -42,19 +43,22 @@ public class StringMarshaller implements CompactBytesMarshaller<String> { @Nullable @Override public String read(@NotNull Bytes bytes) { - if (bytes.readUTFΔ(reader)) - return builderToString(); + StringBuilder sb = sbp.acquireStringBuilder(); + if (bytes.readUTFΔ(sb)) + return builderToString(sb); return null; } - - private String builderToString() { - if (interner == null) + private String builderToString(StringBuilder reader) { + if (interner == null) { + if (size == 0) + return reader.toString(); interner = new StringInterner(size); + } return interner.intern(reader); } public byte code() { - return 'S' & 31; + return STRING_CODE; } } diff --git a/lang/src/main/java/net/openhft/lang/io/serialization/impl/StringZMapMarshaller.java b/lang/src/main/java/net/openhft/lang/io/serialization/impl/StringZMapMarshaller.java new file mode 100755 index 0000000..efec05e --- /dev/null +++ b/lang/src/main/java/net/openhft/lang/io/serialization/impl/StringZMapMarshaller.java @@ -0,0 +1,121 @@ +/* + * Copyright (C) 2015 higherfrequencytrading.com + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as published by + * the Free Software Foundation, either version 3 of the License. + * + * 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 Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with this program. If not, see <http://www.gnu.org/licenses/>. + */ + +package net.openhft.lang.io.serialization.impl; + +import net.openhft.lang.io.Bytes; +import net.openhft.lang.io.serialization.CompactBytesMarshaller; +import net.openhft.lang.model.constraints.Nullable; + +import java.io.DataInputStream; +import java.io.DataOutputStream; +import java.io.IOException; +import java.io.StreamCorruptedException; +import java.util.LinkedHashMap; +import java.util.Map; +import java.util.zip.Deflater; +import java.util.zip.DeflaterOutputStream; +import java.util.zip.InflaterInputStream; + +/** + * Created by peter.lawrey on 24/10/14. + */ +public enum StringZMapMarshaller implements CompactBytesMarshaller<Map<String, String>> { + FAST(Deflater.BEST_SPEED), + COMPACT(Deflater.BEST_COMPRESSION), + INSTANCE(Deflater.DEFAULT_STRATEGY); + + private final int level; + + private static final long NULL_SIZE = -1; + + StringZMapMarshaller(int level) { + this.level = level; + } + + @Override + public byte code() { + return STRINGZ_MAP_CODE; + } + + @Override + public void write(Bytes bytes, Map<String, String> kvMap) { + if (kvMap == null) { + bytes.writeStopBit(NULL_SIZE); + return; + } + + bytes.writeStopBit(kvMap.size()); + long position = bytes.position(); + bytes.clear(); + bytes.position(position + 4); + DataOutputStream dos = new DataOutputStream( + new DeflaterOutputStream(bytes.outputStream(), new Deflater(level))); + try { + for (Map.Entry<String, String> entry : kvMap.entrySet()) { + dos.writeUTF(entry.getKey()); + dos.writeUTF(entry.getValue()); + } + dos.close(); + } catch (IOException e) { + throw new IllegalStateException(e); + } + bytes.writeUnsignedInt(position, bytes.position() - position - 4); + } + + @Override + public Map<String, String> read(Bytes bytes) { + return read(bytes, null); + } + + @Override + public Map<String, String> read(Bytes bytes, @Nullable Map<String, String> kvMap) { + long size = bytes.readStopBit(); + if (size == NULL_SIZE) + return null; + if (size < 0 || size > Integer.MAX_VALUE) + throw new IllegalStateException("Invalid length: " + size); + + long length = bytes.readUnsignedInt(); + if (length < 0 || length > Integer.MAX_VALUE) + throw new IllegalStateException(new StreamCorruptedException()); + long position = bytes.position(); + long end = position + length; + + long limit = bytes.limit(); + bytes.limit(end); + + DataInputStream dis = new DataInputStream(new InflaterInputStream(bytes.inputStream())); + if (kvMap == null) { + kvMap = new LinkedHashMap<String, String>(); + + } else { + kvMap.clear(); + } + try { + for (int i = 0; i < size; i++) { + String key = dis.readUTF(); + String value = dis.readUTF(); + kvMap.put(key, value); + } + } catch (IOException e) { + throw new IllegalStateException(e); + } + bytes.position(end); + bytes.limit(limit); + return kvMap; + } +} diff --git a/lang/src/main/java/net/openhft/lang/io/serialization/impl/VanillaBytesMarshallerFactory.java b/lang/src/main/java/net/openhft/lang/io/serialization/impl/VanillaBytesMarshallerFactory.java index 6fc66ee..3e8d94a 100644 --- a/lang/src/main/java/net/openhft/lang/io/serialization/impl/VanillaBytesMarshallerFactory.java +++ b/lang/src/main/java/net/openhft/lang/io/serialization/impl/VanillaBytesMarshallerFactory.java @@ -1,17 +1,17 @@ /* - * Copyright 2013 Peter Lawrey + * Copyright (C) 2015 higherfrequencytrading.com * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as published by + * the Free Software Foundation, either version 3 of the License. * - * http://www.apache.org/licenses/LICENSE-2.0 + * 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 Lesser General Public License for more details. * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. + * You should have received a copy of the GNU Lesser General Public License + * along with this program. If not, see <http://www.gnu.org/licenses/>. */ package net.openhft.lang.io.serialization.impl; @@ -20,63 +20,92 @@ import net.openhft.lang.io.serialization.BytesMarshallable; import net.openhft.lang.io.serialization.BytesMarshaller; import net.openhft.lang.io.serialization.BytesMarshallerFactory; import net.openhft.lang.io.serialization.CompactBytesMarshaller; -import org.jetbrains.annotations.NotNull; +import net.openhft.lang.model.constraints.NotNull; import java.io.Externalizable; +import java.nio.ByteBuffer; import java.util.Date; import java.util.LinkedHashMap; import java.util.Map; +import static net.openhft.lang.io.serialization.CompactBytesMarshaller.*; + /** * @author peter.lawrey */ -public class VanillaBytesMarshallerFactory implements BytesMarshallerFactory { +public final class VanillaBytesMarshallerFactory implements BytesMarshallerFactory { + private static final long serialVersionUID = 1L; + + private transient Map<Class<?>, BytesMarshaller<?>> marshallerMap; + private transient BytesMarshaller<?>[] compactMarshallerMap; - private final Map<Class, BytesMarshaller> marshallerMap = new LinkedHashMap<Class, BytesMarshaller>(); - private final BytesMarshaller[] compactMarshallerMap = new BytesMarshaller[256]; + private void init() { + marshallerMap = new LinkedHashMap<Class<?>, BytesMarshaller<?>>(); + compactMarshallerMap = new BytesMarshaller[256]; - // private final Map<Class, BytesMarshaller> marshallerTextMap = new LinkedHashMap<Class, BytesMarshaller>(); - { - BytesMarshaller stringMarshaller = new StringMarshaller(16 * 1024); + BytesMarshaller<String> stringMarshaller = new StringMarshaller(16 * 1024); addMarshaller(String.class, stringMarshaller); - addMarshaller(CharSequence.class, stringMarshaller); + addMarshaller(CharSequence.class, (BytesMarshaller)stringMarshaller); addMarshaller(Class.class, new ClassMarshaller(Thread.currentThread().getContextClassLoader())); addMarshaller(Date.class, new DateMarshaller(10191)); - addMarshaller(Integer.class, new CompactEnumBytesMarshaller<Integer>(Integer.class, 10191, (byte) ('I' & 31))); - addMarshaller(Long.class, new CompactEnumBytesMarshaller<Long>(Long.class, 10191, (byte) ('L' & 31))); - addMarshaller(Double.class, new CompactEnumBytesMarshaller<Double>(Double.class, 10191, (byte) ('D' & 31))); + addMarshaller(Integer.class, new CompactEnumBytesMarshaller<Integer>(Integer.class, 10191, INT_CODE)); + addMarshaller(Long.class, new CompactEnumBytesMarshaller<Long>(Long.class, 10191, LONG_CODE)); + addMarshaller(Double.class, new CompactEnumBytesMarshaller<Double>(Double.class, 10191, DOUBLE_CODE)); + addMarshaller(ByteBuffer.class, ByteBufferMarshaller.INSTANCE); } @NotNull @SuppressWarnings("unchecked") @Override public <E> BytesMarshaller<E> acquireMarshaller(@NotNull Class<E> eClass, boolean create) { + if (marshallerMap == null) { + init(); + } + BytesMarshaller em = marshallerMap.get(eClass); - if (em == null) - if (eClass.isEnum()) + if (em == null) { + if (eClass.isEnum()) { marshallerMap.put(eClass, em = new EnumBytesMarshaller(eClass, null)); - else if (BytesMarshallable.class.isAssignableFrom(eClass)) + + } else if (BytesMarshallable.class.isAssignableFrom(eClass)) { marshallerMap.put(eClass, em = new BytesMarshallableMarshaller((Class) eClass)); - else if (Externalizable.class.isAssignableFrom(eClass)) + + } else if (Externalizable.class.isAssignableFrom(eClass)) { marshallerMap.put(eClass, em = new ExternalizableMarshaller((Class) eClass)); - else { + + } else if (Throwable.class.isAssignableFrom(eClass)) { + marshallerMap.put(eClass, em = NoMarshaller.INSTANCE); + + } else { try { marshallerMap.put(eClass, em = new GenericEnumMarshaller<E>(eClass, 1000)); } catch (Exception e) { marshallerMap.put(eClass, em = NoMarshaller.INSTANCE); } } + } + return em; } @Override + @SuppressWarnings("unchecked") public <E> BytesMarshaller<E> getMarshaller(byte code) { - return compactMarshallerMap[code & 0xFF]; + if (marshallerMap == null) { + init(); + } + + return (BytesMarshaller<E>) compactMarshallerMap[code & 0xFF]; } public <E> void addMarshaller(Class<E> eClass, BytesMarshaller<E> marshaller) { + if (marshallerMap == null) { + init(); + } + marshallerMap.put(eClass, marshaller); - if (marshaller instanceof CompactBytesMarshaller) + if (marshaller instanceof CompactBytesMarshaller) { compactMarshallerMap[((CompactBytesMarshaller) marshaller).code()] = marshaller; + } } } diff --git a/lang/src/main/java/net/openhft/lang/io/view/BytesInputStream.java b/lang/src/main/java/net/openhft/lang/io/view/BytesInputStream.java new file mode 100644 index 0000000..4896026 --- /dev/null +++ b/lang/src/main/java/net/openhft/lang/io/view/BytesInputStream.java @@ -0,0 +1,119 @@ +/* + * Copyright (C) 2015 higherfrequencytrading.com + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as published by + * the Free Software Foundation, either version 3 of the License. + * + * 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 Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with this program. If not, see <http://www.gnu.org/licenses/>. + */ + +package net.openhft.lang.io.view; + +import net.openhft.lang.io.Bytes; +import org.jetbrains.annotations.NotNull; + +import java.io.InputStream; + +/** + * {@code InputStream} view of {@link Bytes}. Reading bytes from this stream pushes {@linkplain + * Bytes#position() position} of the underlying {@code Bytes}. When {@linkplain Bytes#limit() limit} + * is reached, {@code BytesInputStream} behaves as there is no more input. + * + * <p>This {@code InputStream} implementation supports {@link #mark(int)} and {@link #reset()} + * methods. + * + * <p>{@code BytesInputStream} objects are reusable, see {@link #bytes(Bytes)} method. + * + * @see Bytes#inputStream() + * @see BytesOutputStream + */ +public class BytesInputStream extends InputStream { + private Bytes bytes; + private long mark = 0; + + /** + * Constructs a {@code BytesInputStream} backed by the given {@code bytes}. + * + * @param bytes the {@code Bytes} backing the constructed {@code BytesInputStream} + */ + public BytesInputStream(Bytes bytes) { + this.bytes = bytes; + } + + /** + * Constructs a {@code BytesInputStream} without backing {@code Bytes}, {@link #bytes(Bytes)} + * method must be called before first actual use of the constructed {@code BytesInputStream} + * instance. + */ + public BytesInputStream() {} + + /** + * Reassigns the underlying {@code Bytes} of this input stream. + * + * @param bytes new {@code Bytes} backing this {@code BytesInputStream} + * @return this {@code BytesInputStream} object back + */ + public BytesInputStream bytes(Bytes bytes) { + this.bytes = bytes; + mark = 0; + return this; + } + + @Override + public int available() { + return bytes.available(); + } + + @Override + public void close() { + bytes.finish(); + } + + @SuppressWarnings("NonSynchronizedMethodOverridesSynchronizedMethod") + @Override + public void mark(int readLimit) { + mark = bytes.position(); + } + + @Override + public boolean markSupported() { + return true; + } + + @Override + public int read(@NotNull byte[] b, int off, int len) { + if (b == null) { + throw new NullPointerException(); + + } else if (off < 0 || len < 0 || len > b.length - off) { + throw new IndexOutOfBoundsException(); + + } else if (len == 0) { + return 0; + } + return bytes.read(b, off, len); + } + + @SuppressWarnings("NonSynchronizedMethodOverridesSynchronizedMethod") + @Override + public void reset() { + bytes.position(mark); + } + + @Override + public long skip(long n) { + return bytes.skip(n); + } + + @Override + public int read() { + return bytes.read(); + } +} diff --git a/lang/src/main/java/net/openhft/lang/io/view/BytesOutputStream.java b/lang/src/main/java/net/openhft/lang/io/view/BytesOutputStream.java new file mode 100644 index 0000000..a05402f --- /dev/null +++ b/lang/src/main/java/net/openhft/lang/io/view/BytesOutputStream.java @@ -0,0 +1,109 @@ +/* + * Copyright (C) 2015 higherfrequencytrading.com + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as published by + * the Free Software Foundation, either version 3 of the License. + * + * 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 Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with this program. If not, see <http://www.gnu.org/licenses/>. + */ + +package net.openhft.lang.io.view; + +import net.openhft.lang.io.Bytes; +import org.jetbrains.annotations.NotNull; + +import java.io.IOException; +import java.io.OutputStream; + +/** + * {@code OutputStream} view of {@link Bytes}. Writing data to this stream pushes {@linkplain + * Bytes#position() position} of the underlying {@code Bytes}. On attempt of writing bytes beyond + * backing {@code Bytes}' {@linkplain Bytes#limit() limit} {@link IOException} is thrown. + * + * <p>{@code BytesOutputStream} are reusable, see {@link #bytes(Bytes)} method. + * + * @see Bytes#outputStream() + * @see BytesInputStream + */ +public class BytesOutputStream extends OutputStream { + private Bytes bytes; + + /** + * Constructs a {@code BytesOutputStream} backed by the given {@code bytes}. + * + * @param bytes the {@code Bytes} backing the constructed {@code BytesOutputStream} + */ + public BytesOutputStream(Bytes bytes) { + this.bytes = bytes; + } + + /** + * Constructs a {@code BytesOutputStream} without backing {@code Bytes}, {@link #bytes(Bytes)} + * method must be called before first actual use of the constructed {@code BytesOutputStream} + * instance. + */ + public BytesOutputStream() {} + + /** + * Reassigns the underlying {@code Bytes} of this output stream. + * + * @param bytes new {@code Bytes} backing this {@code BytesOutputStream} + * @return this {@code BytesOutputStream} object back + */ + public BytesOutputStream bytes(Bytes bytes) { + this.bytes = bytes; + return this; + } + + @Override + public void close() { + try { + super.close(); + } catch (IOException e) { + // never happens. + throw new AssertionError(e); + } + // Don't close as we may want to continue' + } + + private void checkNotClosed() throws IOException { + if (bytes.isFinished()) + throw new IOException("Underlying bytes is closed"); + } + + private void checkAvailable(int n) throws IOException { + if (n > bytes.remaining()) + throw new IOException("Not enough available space for writing " + n + " bytes"); + } + + @Override + public void write(@NotNull byte[] b, int off, int len) throws IOException { + if (b == null) { + throw new NullPointerException(); + + } else if ((off < 0) || (off > b.length) || (len < 0) || + ((off + len) > b.length) || ((off + len) < 0)) { + throw new IndexOutOfBoundsException(); + + } else if (len == 0) { + return; + } + checkNotClosed(); + checkAvailable(len); + bytes.write(b, off, len); + } + + @Override + public void write(int b) throws IOException { + checkNotClosed(); + checkAvailable(1); + bytes.writeUnsignedByte(b); + } +} diff --git a/lang/src/main/java/net/openhft/lang/locks/AbstractReadWriteLockState.java b/lang/src/main/java/net/openhft/lang/locks/AbstractReadWriteLockState.java new file mode 100644 index 0000000..6fc8ae0 --- /dev/null +++ b/lang/src/main/java/net/openhft/lang/locks/AbstractReadWriteLockState.java @@ -0,0 +1,30 @@ +/* + * Copyright (C) 2015 higherfrequencytrading.com + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as published by + * the Free Software Foundation, either version 3 of the License. + * + * 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 Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with this program. If not, see <http://www.gnu.org/licenses/>. + */ + +package net.openhft.lang.locks; + +public abstract class AbstractReadWriteLockState implements ReadWriteLockState { + + @Override + public boolean tryLock() { + return tryWriteLock(); + } + + @Override + public void unlock() { + writeUnlock(); + } +} diff --git a/lang/src/main/java/net/openhft/lang/locks/AbstractReadWriteLockingStrategy.java b/lang/src/main/java/net/openhft/lang/locks/AbstractReadWriteLockingStrategy.java new file mode 100644 index 0000000..2c4fd2a --- /dev/null +++ b/lang/src/main/java/net/openhft/lang/locks/AbstractReadWriteLockingStrategy.java @@ -0,0 +1,35 @@ +/* + * Copyright (C) 2015 higherfrequencytrading.com + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as published by + * the Free Software Foundation, either version 3 of the License. + * + * 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 Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with this program. If not, see <http://www.gnu.org/licenses/>. + */ + +package net.openhft.lang.locks; + +public abstract class AbstractReadWriteLockingStrategy implements ReadWriteLockingStrategy { + + @Override + public <T> boolean tryLock(NativeAtomicAccess<T> access, T t, long offset) { + return tryWriteLock(access, t, offset); + } + + @Override + public <T> void unlock(NativeAtomicAccess<T> access, T t, long offset) { + writeUnlock(access, t, offset); + } + + @Override + public boolean isReadLocked(long state) { + return readLockCount(state) > 0; + } +} diff --git a/lang/src/main/java/net/openhft/lang/locks/AcquisitionStrategies.java b/lang/src/main/java/net/openhft/lang/locks/AcquisitionStrategies.java new file mode 100644 index 0000000..7a1c234 --- /dev/null +++ b/lang/src/main/java/net/openhft/lang/locks/AcquisitionStrategies.java @@ -0,0 +1,106 @@ +/* + * Copyright (C) 2015 higherfrequencytrading.com + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as published by + * the Free Software Foundation, either version 3 of the License. + * + * 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 Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with this program. If not, see <http://www.gnu.org/licenses/>. + */ + +package net.openhft.lang.locks; + +import java.util.concurrent.TimeUnit; + +public final class AcquisitionStrategies { + + private static class SpinLoopAcquisitionStrategy<S extends LockingStrategy> + implements AcquisitionStrategy<S, RuntimeException> { + private final long durationNanos; + + private SpinLoopAcquisitionStrategy(long duration, TimeUnit unit) { + durationNanos = unit.toNanos(duration); + } + + @Override + public <T> boolean acquire(TryAcquireOperation<? super S> operation, S strategy, + NativeAtomicAccess<T> access, T t, long offset) { + if (operation.tryAcquire(strategy, access, t, offset)) + return true; + long deadLine = System.currentTimeMillis() + durationNanos; + beforeLoop(strategy, access, t, offset); + do { + if (operation.tryAcquire(strategy, access, t, offset)) + return true; + } while (deadLine - System.currentTimeMillis() >= 0L); // overflow-cautious + afterLoop(strategy, access, t, offset); + return end(); + } + + <T> void beforeLoop(S strategy, NativeAtomicAccess<T> access, T t, long offset) { + } + + <T> void afterLoop(S strategy, NativeAtomicAccess<T> access, T t, long offset) { + } + + boolean end() { + return false; + } + } + + public static AcquisitionStrategy<LockingStrategy, RuntimeException> spinLoop( + long duration, TimeUnit unit) { + return new SpinLoopAcquisitionStrategy<LockingStrategy>(duration, unit); + } + + private static class SpinLoopOrFailAcquisitionStrategy<S extends LockingStrategy> + extends SpinLoopAcquisitionStrategy<S> { + + private SpinLoopOrFailAcquisitionStrategy(long duration, TimeUnit unit) { + super(duration, unit); + } + + @Override + boolean end() { + throw new RuntimeException("Failed to acquire the lock"); + } + } + + public static AcquisitionStrategy<LockingStrategy, RuntimeException> spinLoopOrFail( + long duration, TimeUnit unit) { + return new SpinLoopOrFailAcquisitionStrategy<LockingStrategy>(duration, unit); + } + + private static class SpinLoopWriteWithWaitsAcquisitionStrategy + extends SpinLoopOrFailAcquisitionStrategy<ReadWriteWithWaitsLockingStrategy> { + + private SpinLoopWriteWithWaitsAcquisitionStrategy(long duration, TimeUnit unit) { + super(duration, unit); + } + + @Override + <T> void beforeLoop(ReadWriteWithWaitsLockingStrategy strategy, + NativeAtomicAccess<T> access, T t, long offset) { + strategy.registerWait(access, t, offset); + } + + @Override + <T> void afterLoop(ReadWriteWithWaitsLockingStrategy strategy, + NativeAtomicAccess<T> access, T t, long offset) { + strategy.deregisterWait(access, t, offset); + } + } + + public static AcquisitionStrategy<ReadWriteWithWaitsLockingStrategy, RuntimeException> + spinLoopRegisteringWaitOrFail(long duration, TimeUnit unit) { + return new SpinLoopWriteWithWaitsAcquisitionStrategy(duration, unit); + } + + private AcquisitionStrategies() {} +} diff --git a/lang/src/main/java/net/openhft/lang/locks/AcquisitionStrategy.java b/lang/src/main/java/net/openhft/lang/locks/AcquisitionStrategy.java new file mode 100644 index 0000000..5a62cd5 --- /dev/null +++ b/lang/src/main/java/net/openhft/lang/locks/AcquisitionStrategy.java @@ -0,0 +1,24 @@ +/* + * Copyright (C) 2015 higherfrequencytrading.com + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as published by + * the Free Software Foundation, either version 3 of the License. + * + * 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 Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with this program. If not, see <http://www.gnu.org/licenses/>. + */ + +package net.openhft.lang.locks; + +public interface AcquisitionStrategy<S extends LockingStrategy, E extends Exception> { + + <T> boolean acquire( + TryAcquireOperation<? super S> operation, S strategy, + NativeAtomicAccess<T> access, T t, long offset) throws E; +} diff --git a/lang/src/main/java/net/openhft/lang/locks/BytesAtomicAccess.java b/lang/src/main/java/net/openhft/lang/locks/BytesAtomicAccess.java new file mode 100644 index 0000000..c0bee33 --- /dev/null +++ b/lang/src/main/java/net/openhft/lang/locks/BytesAtomicAccess.java @@ -0,0 +1,73 @@ +/* + * Copyright (C) 2015 higherfrequencytrading.com + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as published by + * the Free Software Foundation, either version 3 of the License. + * + * 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 Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with this program. If not, see <http://www.gnu.org/licenses/>. + */ + +package net.openhft.lang.locks; + +import net.openhft.lang.io.Bytes; + +import static java.nio.ByteOrder.nativeOrder; + +final class BytesAtomicAccess extends NativeAtomicAccess<Bytes> { + static final BytesAtomicAccess INSTANCE = new BytesAtomicAccess(); + + @Override + public long getLongVolatile(Bytes bytes, long offset) { + long value = bytes.readVolatileLong(offset); + if (bytes.byteOrder() != nativeOrder()) + value = Long.reverseBytes(value); + return value; + } + + @Override + public void putOrderedLong(Bytes bytes, long offset, long value) { + if (bytes.byteOrder() != nativeOrder()) + value = Long.reverseBytes(value); + bytes.writeOrderedLong(offset, value); + } + + @Override + public boolean compareAndSwapLong(Bytes bytes, long offset, long expected, long x) { + if (bytes.byteOrder() != nativeOrder()) { + expected = Long.reverseBytes(expected); + x = Long.reverseBytes(x); + } + return bytes.compareAndSwapLong(offset, expected, x); + } + + @Override + public int getIntVolatile(Bytes bytes, long offset) { + int value = bytes.readVolatileInt(offset); + if (bytes.byteOrder() != nativeOrder()) + value = Integer.reverseBytes(value); + return value; + } + + @Override + public void putOrderedInt(Bytes bytes, long offset, int value) { + if (bytes.byteOrder() != nativeOrder()) + value = Integer.reverseBytes(value); + bytes.writeOrderedInt(offset, value); + } + + @Override + public boolean compareAndSwapInt(Bytes bytes, long offset, int expected, int x) { + if (bytes.byteOrder() != nativeOrder()) { + expected = Integer.reverseBytes(expected); + x = Integer.reverseBytes(x); + } + return bytes.compareAndSwapInt(offset, expected, x); + } +} diff --git a/lang/src/main/java/net/openhft/lang/locks/LockState.java b/lang/src/main/java/net/openhft/lang/locks/LockState.java new file mode 100644 index 0000000..a6706df --- /dev/null +++ b/lang/src/main/java/net/openhft/lang/locks/LockState.java @@ -0,0 +1,30 @@ +/* + * Copyright (C) 2015 higherfrequencytrading.com + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as published by + * the Free Software Foundation, either version 3 of the License. + * + * 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 Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with this program. If not, see <http://www.gnu.org/licenses/>. + */ + +package net.openhft.lang.locks; + +public interface LockState { + + boolean tryLock(); + + void unlock(); + + void reset(); + + long getState(); + + LockingStrategy lockingStrategy(); +} diff --git a/lang/src/main/java/net/openhft/lang/locks/LockingStrategy.java b/lang/src/main/java/net/openhft/lang/locks/LockingStrategy.java new file mode 100644 index 0000000..ce502bb --- /dev/null +++ b/lang/src/main/java/net/openhft/lang/locks/LockingStrategy.java @@ -0,0 +1,38 @@ +/* + * Copyright (C) 2015 higherfrequencytrading.com + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as published by + * the Free Software Foundation, either version 3 of the License. + * + * 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 Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with this program. If not, see <http://www.gnu.org/licenses/>. + */ + +package net.openhft.lang.locks; + +public interface LockingStrategy { + + <T> boolean tryLock(NativeAtomicAccess<T> access, T t, long offset); + + <T> void unlock(NativeAtomicAccess<T> access, T t, long offset); + + <T> void reset(NativeAtomicAccess<T> access, T t, long offset); + + long resetState(); + + <T> long getState(NativeAtomicAccess<T> access, T t, long offset); + + boolean isLocked(long state); + + int lockCount(long state); + + String toString(long state); + + int sizeInBytes(); +} diff --git a/lang/src/main/java/net/openhft/lang/locks/NativeAtomicAccess.java b/lang/src/main/java/net/openhft/lang/locks/NativeAtomicAccess.java new file mode 100644 index 0000000..0b47ea6 --- /dev/null +++ b/lang/src/main/java/net/openhft/lang/locks/NativeAtomicAccess.java @@ -0,0 +1,44 @@ +/* + * Copyright (C) 2015 higherfrequencytrading.com + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as published by + * the Free Software Foundation, either version 3 of the License. + * + * 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 Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with this program. If not, see <http://www.gnu.org/licenses/>. + */ + +package net.openhft.lang.locks; + +import net.openhft.lang.io.Bytes; + +public abstract class NativeAtomicAccess<T> { + + public static <T> NativeAtomicAccess<T> unsafe() { + return UnsafeAtomicAccess.INSTANCE; + } + + public static NativeAtomicAccess<Bytes> toBytes() { + return BytesAtomicAccess.INSTANCE; + } + + protected NativeAtomicAccess() {} + + public abstract long getLongVolatile(T t, long offset); + + public abstract void putOrderedLong(T t, long offset, long value); + + public abstract boolean compareAndSwapLong(T t, long offset, long expected, long x); + + public abstract int getIntVolatile(T t, long offset); + + public abstract void putOrderedInt(T t, long offset, int value); + + public abstract boolean compareAndSwapInt(T t, long offset, int expected, int x); +} diff --git a/lang/src/main/java/net/openhft/lang/locks/ReadWriteLockState.java b/lang/src/main/java/net/openhft/lang/locks/ReadWriteLockState.java new file mode 100644 index 0000000..fe3127a --- /dev/null +++ b/lang/src/main/java/net/openhft/lang/locks/ReadWriteLockState.java @@ -0,0 +1,35 @@ +/* + * Copyright (C) 2015 higherfrequencytrading.com + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as published by + * the Free Software Foundation, either version 3 of the License. + * + * 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 Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with this program. If not, see <http://www.gnu.org/licenses/>. + */ + +package net.openhft.lang.locks; + +public interface ReadWriteLockState extends LockState { + + boolean tryReadLock(); + + boolean tryWriteLock(); + + boolean tryUpgradeReadToWriteLock(); + + void readUnlock(); + + void writeUnlock(); + + void downgradeWriteToReadLock(); + + @Override + ReadWriteLockingStrategy lockingStrategy(); +} diff --git a/lang/src/main/java/net/openhft/lang/locks/ReadWriteLockingStrategy.java b/lang/src/main/java/net/openhft/lang/locks/ReadWriteLockingStrategy.java new file mode 100644 index 0000000..bb6179d --- /dev/null +++ b/lang/src/main/java/net/openhft/lang/locks/ReadWriteLockingStrategy.java @@ -0,0 +1,38 @@ +/* + * Copyright (C) 2015 higherfrequencytrading.com + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as published by + * the Free Software Foundation, either version 3 of the License. + * + * 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 Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with this program. If not, see <http://www.gnu.org/licenses/>. + */ + +package net.openhft.lang.locks; + +public interface ReadWriteLockingStrategy extends LockingStrategy { + + <T> boolean tryReadLock(NativeAtomicAccess<T> access, T t, long offset); + + <T> boolean tryWriteLock(NativeAtomicAccess<T> access, T t, long offset); + + <T> boolean tryUpgradeReadToWriteLock(NativeAtomicAccess<T> access, T t, long offset); + + <T> void readUnlock(NativeAtomicAccess<T> access, T t, long offset); + + <T> void writeUnlock(NativeAtomicAccess<T> access, T t, long offset); + + <T> void downgradeWriteToReadLock(NativeAtomicAccess<T> access, T t, long offset); + + boolean isReadLocked(long state); + + boolean isWriteLocked(long state); + + int readLockCount(long state); +} diff --git a/lang/src/main/java/net/openhft/lang/locks/ReadWriteUpdateLockState.java b/lang/src/main/java/net/openhft/lang/locks/ReadWriteUpdateLockState.java new file mode 100644 index 0000000..584ab1d --- /dev/null +++ b/lang/src/main/java/net/openhft/lang/locks/ReadWriteUpdateLockState.java @@ -0,0 +1,35 @@ +/* + * Copyright (C) 2015 higherfrequencytrading.com + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as published by + * the Free Software Foundation, either version 3 of the License. + * + * 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 Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with this program. If not, see <http://www.gnu.org/licenses/>. + */ + +package net.openhft.lang.locks; + +public interface ReadWriteUpdateLockState extends ReadWriteLockState { + + boolean tryUpdateLock(); + + boolean tryUpgradeReadToUpdateLock(); + + boolean tryUpgradeUpdateToWriteLock(); + + void updateUnlock(); + + void downgradeUpdateToReadLock(); + + void downgradeWriteToUpdateLock(); + + @Override + ReadWriteUpdateLockingStrategy lockingStrategy(); +} diff --git a/lang/src/main/java/net/openhft/lang/locks/ReadWriteUpdateLockingStrategy.java b/lang/src/main/java/net/openhft/lang/locks/ReadWriteUpdateLockingStrategy.java new file mode 100644 index 0000000..55bab01 --- /dev/null +++ b/lang/src/main/java/net/openhft/lang/locks/ReadWriteUpdateLockingStrategy.java @@ -0,0 +1,41 @@ +/* + * Copyright (C) 2015 higherfrequencytrading.com + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as published by + * the Free Software Foundation, either version 3 of the License. + * + * 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 Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with this program. If not, see <http://www.gnu.org/licenses/>. + */ + +package net.openhft.lang.locks; + +/** + * Logic of read-write-update lock state transitions. + * + * Read lock - could be several at the same time. + * Update lock - doesn't block reads, but couldn't be several update locks at the same time + * Write lock - exclusive + */ +public interface ReadWriteUpdateLockingStrategy extends ReadWriteLockingStrategy { + + <T> boolean tryUpdateLock(NativeAtomicAccess<T> access, T t, long offset); + + <T> boolean tryUpgradeReadToUpdateLock(NativeAtomicAccess<T> access, T t, long offset); + + <T> boolean tryUpgradeUpdateToWriteLock(NativeAtomicAccess<T> access, T t, long offset); + + <T> void updateUnlock(NativeAtomicAccess<T> access, T t, long offset); + + <T> void downgradeUpdateToReadLock(NativeAtomicAccess<T> access, T t, long offset); + + <T> void downgradeWriteToUpdateLock(NativeAtomicAccess<T> access, T t, long offset); + + boolean isUpdateLocked(long state); +} diff --git a/lang/src/main/java/net/openhft/lang/locks/ReadWriteUpdateWithWaitsLockState.java b/lang/src/main/java/net/openhft/lang/locks/ReadWriteUpdateWithWaitsLockState.java new file mode 100644 index 0000000..ad5cf05 --- /dev/null +++ b/lang/src/main/java/net/openhft/lang/locks/ReadWriteUpdateWithWaitsLockState.java @@ -0,0 +1,26 @@ +/* + * Copyright (C) 2015 higherfrequencytrading.com + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as published by + * the Free Software Foundation, either version 3 of the License. + * + * 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 Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with this program. If not, see <http://www.gnu.org/licenses/>. + */ + +package net.openhft.lang.locks; + +public interface ReadWriteUpdateWithWaitsLockState + extends ReadWriteUpdateLockState, ReadWriteWithWaitsLockState { + + boolean tryUpgradeUpdateToWriteLockAndDeregisterWait(); + + @Override + ReadWriteUpdateWithWaitsLockingStrategy lockingStrategy(); +} diff --git a/lang/src/main/java/net/openhft/lang/locks/ReadWriteUpdateWithWaitsLockingStrategy.java b/lang/src/main/java/net/openhft/lang/locks/ReadWriteUpdateWithWaitsLockingStrategy.java new file mode 100644 index 0000000..aa58ef7 --- /dev/null +++ b/lang/src/main/java/net/openhft/lang/locks/ReadWriteUpdateWithWaitsLockingStrategy.java @@ -0,0 +1,24 @@ +/* + * Copyright (C) 2015 higherfrequencytrading.com + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as published by + * the Free Software Foundation, either version 3 of the License. + * + * 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 Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with this program. If not, see <http://www.gnu.org/licenses/>. + */ + +package net.openhft.lang.locks; + +public interface ReadWriteUpdateWithWaitsLockingStrategy + extends ReadWriteUpdateLockingStrategy, ReadWriteWithWaitsLockingStrategy { + + <T> boolean tryUpgradeUpdateToWriteLockAndDeregisterWait( + NativeAtomicAccess<T> access, T t, long offset); +} diff --git a/lang/src/main/java/net/openhft/lang/locks/ReadWriteWithWaitsLockState.java b/lang/src/main/java/net/openhft/lang/locks/ReadWriteWithWaitsLockState.java new file mode 100644 index 0000000..d95d33a --- /dev/null +++ b/lang/src/main/java/net/openhft/lang/locks/ReadWriteWithWaitsLockState.java @@ -0,0 +1,33 @@ +/* + * Copyright (C) 2015 higherfrequencytrading.com + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as published by + * the Free Software Foundation, either version 3 of the License. + * + * 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 Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with this program. If not, see <http://www.gnu.org/licenses/>. + */ + +package net.openhft.lang.locks; + +public interface ReadWriteWithWaitsLockState extends ReadWriteLockState { + + void registerWait(); + + void deregisterWait(); + + boolean tryWriteLockAndDeregisterWait(); + + boolean tryUpgradeReadToWriteLockAndDeregisterWait(); + + void resetKeepingWaits(); + + @Override + ReadWriteWithWaitsLockingStrategy lockingStrategy(); +} diff --git a/lang/src/main/java/net/openhft/lang/locks/ReadWriteWithWaitsLockingStrategy.java b/lang/src/main/java/net/openhft/lang/locks/ReadWriteWithWaitsLockingStrategy.java new file mode 100644 index 0000000..8db8b9d --- /dev/null +++ b/lang/src/main/java/net/openhft/lang/locks/ReadWriteWithWaitsLockingStrategy.java @@ -0,0 +1,33 @@ +/* + * Copyright (C) 2015 higherfrequencytrading.com + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as published by + * the Free Software Foundation, either version 3 of the License. + * + * 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 Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with this program. If not, see <http://www.gnu.org/licenses/>. + */ + +package net.openhft.lang.locks; + +public interface ReadWriteWithWaitsLockingStrategy extends ReadWriteLockingStrategy { + + <T> void registerWait(NativeAtomicAccess<T> access, T t, long offset); + + <T> void deregisterWait(NativeAtomicAccess<T> access, T t, long offset); + + <T> boolean tryWriteLockAndDeregisterWait(NativeAtomicAccess<T> access, T t, long offset); + + <T> boolean tryUpgradeReadToWriteLockAndDeregisterWait( + NativeAtomicAccess<T> access, T t, long offset); + + <T> void resetKeepingWaits(NativeAtomicAccess<T> access, T t, long offset); + + int waitCount(long state); +} diff --git a/lang/src/main/java/net/openhft/lang/locks/TryAcquireOperation.java b/lang/src/main/java/net/openhft/lang/locks/TryAcquireOperation.java new file mode 100644 index 0000000..78a9e49 --- /dev/null +++ b/lang/src/main/java/net/openhft/lang/locks/TryAcquireOperation.java @@ -0,0 +1,22 @@ +/* + * Copyright (C) 2015 higherfrequencytrading.com + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as published by + * the Free Software Foundation, either version 3 of the License. + * + * 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 Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with this program. If not, see <http://www.gnu.org/licenses/>. + */ + +package net.openhft.lang.locks; + +public interface TryAcquireOperation<S extends LockingStrategy> { + + <T> boolean tryAcquire(S strategy, NativeAtomicAccess<T> access, T t, long offset); +}
\ No newline at end of file diff --git a/lang/src/main/java/net/openhft/lang/locks/TryAcquireOperations.java b/lang/src/main/java/net/openhft/lang/locks/TryAcquireOperations.java new file mode 100644 index 0000000..5d7f391 --- /dev/null +++ b/lang/src/main/java/net/openhft/lang/locks/TryAcquireOperations.java @@ -0,0 +1,152 @@ +/* + * Copyright (C) 2015 higherfrequencytrading.com + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as published by + * the Free Software Foundation, either version 3 of the License. + * + * 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 Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with this program. If not, see <http://www.gnu.org/licenses/>. + */ + +package net.openhft.lang.locks; + +public final class TryAcquireOperations { + + private static final TryAcquireOperation<LockingStrategy> LOCK = + new TryAcquireOperation<LockingStrategy>() { + @Override + public <T> boolean tryAcquire(LockingStrategy strategy, + NativeAtomicAccess<T> access, T obj, long offset) { + return strategy.tryLock(access, obj, offset); + } + }; + public static TryAcquireOperation<LockingStrategy> lock() { + return LOCK; + } + + private static final TryAcquireOperation<ReadWriteLockingStrategy> READ_LOCK = + new TryAcquireOperation<ReadWriteLockingStrategy>() { + @Override + public <T> boolean tryAcquire(ReadWriteLockingStrategy strategy, + NativeAtomicAccess<T> access, T obj, long offset) { + return strategy.tryReadLock(access, obj, offset); + } + }; + public static TryAcquireOperation<ReadWriteLockingStrategy> readLock() { + return READ_LOCK; + } + + private static final TryAcquireOperation<ReadWriteLockingStrategy> UPGRADE_READ_TO_WRITE_LOCK = + new TryAcquireOperation<ReadWriteLockingStrategy>() { + @Override + public <T> boolean tryAcquire(ReadWriteLockingStrategy strategy, + NativeAtomicAccess<T> access, T obj, long offset) { + return strategy.tryUpgradeReadToWriteLock(access, obj, offset); + } + }; + public static TryAcquireOperation<ReadWriteLockingStrategy> upgradeReadToWriteLock() { + return UPGRADE_READ_TO_WRITE_LOCK; + } + + private static final TryAcquireOperation<ReadWriteLockingStrategy> WRITE_LOCK = + new TryAcquireOperation<ReadWriteLockingStrategy>() { + @Override + public <T> boolean tryAcquire(ReadWriteLockingStrategy strategy, + NativeAtomicAccess<T> access, T obj, long offset) { + return strategy.tryWriteLock(access, obj, offset); + } + }; + public static TryAcquireOperation<ReadWriteLockingStrategy> writeLock() { + return WRITE_LOCK; + } + + private static final TryAcquireOperation<ReadWriteWithWaitsLockingStrategy> + UPGRADE_READ_TO_WRITE_LOCK_AND_DEREGISTER_WAIT = + new TryAcquireOperation<ReadWriteWithWaitsLockingStrategy>() { + @Override + public <T> boolean tryAcquire(ReadWriteWithWaitsLockingStrategy strategy, + NativeAtomicAccess<T> access, T obj, long offset) { + return strategy.tryUpgradeReadToWriteLockAndDeregisterWait(access, obj, offset); + } + }; + public static TryAcquireOperation<ReadWriteWithWaitsLockingStrategy> + upgradeReadToWriteLockAndDeregisterWait() { + return UPGRADE_READ_TO_WRITE_LOCK_AND_DEREGISTER_WAIT; + } + + private static final TryAcquireOperation<ReadWriteWithWaitsLockingStrategy> + WRITE_LOCK_AND_DEREGISTER_WAIT = + new TryAcquireOperation<ReadWriteWithWaitsLockingStrategy>() { + @Override + public <T> boolean tryAcquire(ReadWriteWithWaitsLockingStrategy strategy, + NativeAtomicAccess<T> access, T obj, long offset) { + return strategy.tryWriteLockAndDeregisterWait(access, obj, offset); + } + }; + public static TryAcquireOperation<ReadWriteWithWaitsLockingStrategy> + writeLockAndDeregisterWait() { + return WRITE_LOCK_AND_DEREGISTER_WAIT; + } + + private static final TryAcquireOperation<ReadWriteUpdateLockingStrategy> UPDATE_LOCK = + new TryAcquireOperation<ReadWriteUpdateLockingStrategy>() { + @Override + public <T> boolean tryAcquire(ReadWriteUpdateLockingStrategy strategy, + NativeAtomicAccess<T> access, T obj, long offset) { + return strategy.tryUpdateLock(access, obj, offset); + } + }; + public static TryAcquireOperation<ReadWriteUpdateLockingStrategy> updateLock() { + return UPDATE_LOCK; + } + + private static final TryAcquireOperation<ReadWriteUpdateLockingStrategy> + UPGRADE_READ_TO_UPDATE_LOCK = + new TryAcquireOperation<ReadWriteUpdateLockingStrategy>() { + @Override + public <T> boolean tryAcquire(ReadWriteUpdateLockingStrategy strategy, + NativeAtomicAccess<T> access, T obj, long offset) { + return strategy.tryUpgradeReadToUpdateLock(access, obj, offset); + } + }; + public static TryAcquireOperation<ReadWriteUpdateLockingStrategy> upgradeReadToUpdateLock() { + return UPGRADE_READ_TO_UPDATE_LOCK; + } + + private static final TryAcquireOperation<ReadWriteUpdateLockingStrategy> + UPGRADE_UPDATE_TO_WRITE_LOCK = + new TryAcquireOperation<ReadWriteUpdateLockingStrategy>() { + @Override + public <T> boolean tryAcquire(ReadWriteUpdateLockingStrategy strategy, + NativeAtomicAccess<T> access, T obj, long offset) { + return strategy.tryUpgradeUpdateToWriteLock(access, obj, offset); + } + }; + public static TryAcquireOperation<ReadWriteUpdateLockingStrategy> upgradeUpdateToWriteLock() { + return UPGRADE_UPDATE_TO_WRITE_LOCK; + } + + private static final TryAcquireOperation<ReadWriteUpdateWithWaitsLockingStrategy> + UPGRADE_UPDATE_TO_WRITE_LOCK_AND_DEREGISTER_WAIT = + new TryAcquireOperation<ReadWriteUpdateWithWaitsLockingStrategy>() { + @Override + public <T> boolean tryAcquire(ReadWriteUpdateWithWaitsLockingStrategy strategy, + NativeAtomicAccess<T> access, T obj, long offset) { + return strategy.tryUpgradeUpdateToWriteLockAndDeregisterWait( + access, obj, offset); + } + }; + public static TryAcquireOperation<ReadWriteUpdateWithWaitsLockingStrategy> + upgradeUpdateToWriteLockAndDeregisterWait() { + return UPGRADE_UPDATE_TO_WRITE_LOCK_AND_DEREGISTER_WAIT; + } + + private TryAcquireOperations() {} +} + diff --git a/lang/src/main/java/net/openhft/lang/locks/UnsafeAtomicAccess.java b/lang/src/main/java/net/openhft/lang/locks/UnsafeAtomicAccess.java new file mode 100644 index 0000000..17c52f2 --- /dev/null +++ b/lang/src/main/java/net/openhft/lang/locks/UnsafeAtomicAccess.java @@ -0,0 +1,53 @@ +/* + * Copyright (C) 2015 higherfrequencytrading.com + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as published by + * the Free Software Foundation, either version 3 of the License. + * + * 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 Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with this program. If not, see <http://www.gnu.org/licenses/>. + */ + +package net.openhft.lang.locks; + +import static net.openhft.lang.io.NativeBytes.UNSAFE; + +final class UnsafeAtomicAccess extends NativeAtomicAccess { + static final UnsafeAtomicAccess INSTANCE = new UnsafeAtomicAccess(); + + @Override + public long getLongVolatile(Object o, long offset) { + return UNSAFE.getLongVolatile(o, offset); + } + + @Override + public void putOrderedLong(Object o, long offset, long value) { + UNSAFE.putOrderedLong(o, offset, value); + } + + @Override + public boolean compareAndSwapLong(Object o, long offset, long expected, long x) { + return UNSAFE.compareAndSwapLong(o, offset, expected, x); + } + + @Override + public int getIntVolatile(Object o, long offset) { + return UNSAFE.getInt(o, offset); + } + + @Override + public void putOrderedInt(Object o, long offset, int value) { + UNSAFE.putOrderedInt(o, offset, value); + } + + @Override + public boolean compareAndSwapInt(Object o, long offset, int expected, int x) { + return UNSAFE.compareAndSwapInt(o, offset, expected, x); + } +} diff --git a/lang/src/main/java/net/openhft/lang/locks/VanillaReadWriteUpdateWithWaitsLockingStrategy.java b/lang/src/main/java/net/openhft/lang/locks/VanillaReadWriteUpdateWithWaitsLockingStrategy.java new file mode 100644 index 0000000..b1979e8 --- /dev/null +++ b/lang/src/main/java/net/openhft/lang/locks/VanillaReadWriteUpdateWithWaitsLockingStrategy.java @@ -0,0 +1,381 @@ +/* + * Copyright (C) 2015 higherfrequencytrading.com + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as published by + * the Free Software Foundation, either version 3 of the License. + * + * 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 Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with this program. If not, see <http://www.gnu.org/licenses/>. + */ + +package net.openhft.lang.locks; + +import static java.nio.ByteOrder.LITTLE_ENDIAN; +import static java.nio.ByteOrder.nativeOrder; +import static net.openhft.lang.io.AbstractBytes.UNSIGNED_INT_MASK; + +public final class VanillaReadWriteUpdateWithWaitsLockingStrategy + extends AbstractReadWriteLockingStrategy + implements ReadWriteUpdateWithWaitsLockingStrategy { + + private static final ReadWriteUpdateWithWaitsLockingStrategy INSTANCE = + new VanillaReadWriteUpdateWithWaitsLockingStrategy(); + + public static ReadWriteUpdateWithWaitsLockingStrategy instance() { + return INSTANCE; + } + + private VanillaReadWriteUpdateWithWaitsLockingStrategy() {} + + static final long COUNT_WORD_OFFSET = 0L; + static final long WAIT_WORD_OFFSET = COUNT_WORD_OFFSET + 4L; + + static final int COUNT_WORD_SHIFT = nativeOrder() == LITTLE_ENDIAN ? 0 : 32; + static final int WAIT_WORD_SHIFT = nativeOrder() == LITTLE_ENDIAN ? 32 : 0; + + static final int READ_BITS = 30; + static final int MAX_READ = (1 << READ_BITS) - 1; + static final int READ_MASK = MAX_READ; + static final int READ_PARTY = 1; + + static final int UPDATE_PARTY = 1 << READ_BITS; + static final int WRITE_LOCKED_COUNT_WORD = UPDATE_PARTY << 1; + + static final int MAX_WAIT = Integer.MAX_VALUE; + static final int WAIT_PARTY = 1; + + private static <T> long getLockWord(NativeAtomicAccess<T> access, T t, long offset) { + return access.getLongVolatile(t, offset); + } + + private static <T> boolean casLockWord( + NativeAtomicAccess<T> access, T t, long offset, long expected, long x) { + return access.compareAndSwapLong(t, offset, expected, x); + } + + private static int countWord(long lockWord) { + return (int) (lockWord >> COUNT_WORD_SHIFT); + } + + private static int waitWord(long lockWord) { + return (int) (lockWord >> WAIT_WORD_SHIFT); + } + + private static long lockWord(int countWord, int waitWord) { + return ((((long) countWord) & UNSIGNED_INT_MASK) << COUNT_WORD_SHIFT) | + ((((long) waitWord) & UNSIGNED_INT_MASK) << WAIT_WORD_SHIFT); + } + + private static <T> int getCountWord(NativeAtomicAccess<T> access, T t, long offset) { + return access.getIntVolatile(t, offset + COUNT_WORD_OFFSET); + } + + private static <T> boolean casCountWord( + NativeAtomicAccess<T> access, T t, long offset, int expected, int x) { + return access.compareAndSwapInt(t, offset + COUNT_WORD_OFFSET, expected, x); + } + + private static <T> void putCountWord( + NativeAtomicAccess<T> access, T t, long offset, int countWord) { + access.putOrderedInt(t, offset + COUNT_WORD_OFFSET, countWord); + } + + private static boolean writeLocked(int countWord) { + return countWord == WRITE_LOCKED_COUNT_WORD; + } + + private static void checkWriteLocked(int countWord) { + if (countWord != WRITE_LOCKED_COUNT_WORD) + throw new IllegalMonitorStateException("Expected write lock"); + } + + private static boolean updateLocked(int countWord) { + return (countWord & UPDATE_PARTY) != 0; + } + + private static void checkUpdateLocked(int countWord) { + if (!updateLocked(countWord)) + throw new IllegalMonitorStateException("Expected update lock"); + } + + private static int readCount(int countWord) { + return countWord & READ_MASK; + } + + private static void checkReadLocked(int countWord) { + if (readCount(countWord) <= 0) + throw new IllegalMonitorStateException("Expected read lock"); + } + + private static void checkReadCountForIncrement(int countWord) { + if (readCount(countWord) == MAX_READ) { + throw new IllegalMonitorStateException( + "Lock count reached the limit of " + MAX_READ); + } + } + + private static <T> int getWaitWord(NativeAtomicAccess<T> access, T t, long offset) { + return access.getIntVolatile(t, offset + WAIT_WORD_OFFSET); + } + + private static <T> boolean casWaitWord( + NativeAtomicAccess<T> access, T t, long offset, int expected, int x) { + return access.compareAndSwapInt(t, offset + WAIT_WORD_OFFSET, expected, x); + } + + private static void checkWaitWordForIncrement(int waitWord) { + if (waitWord == MAX_WAIT) { + throw new IllegalMonitorStateException( + "Wait count reached the limit of " + MAX_WAIT); + } + } + + private static void checkWaitWordForDecrement(int waitWord) { + if (waitWord == 0) { + throw new IllegalMonitorStateException( + "Wait count underflowed"); + } + } + + @Override + public long resetState() { + return 0L; + } + + @Override + public <T> void reset(NativeAtomicAccess<T> access, T t, long offset) { + access.putOrderedLong(t, offset, 0L); + } + + @Override + public <T> void resetKeepingWaits(NativeAtomicAccess<T> access, T t, long offset) { + putCountWord(access, t, offset, 0); + } + + @Override + public <T> boolean tryReadLock(NativeAtomicAccess<T> access, T t, long offset) { + long lockWord = getLockWord(access, t, offset); + int countWord = countWord(lockWord); + if (!writeLocked(countWord) && waitWord(lockWord) == 0) { + checkReadCountForIncrement(countWord); + if (casCountWord(access, t, offset, countWord, countWord + READ_PARTY)) + return true; + } + return false; + } + + @Override + public <T> boolean tryUpgradeReadToUpdateLock(NativeAtomicAccess<T> access, T t, long offset) { + int countWord = getCountWord(access, t, offset); + checkReadLocked(countWord); + return !updateLocked(countWord) && + casCountWord(access, t, offset, countWord, countWord - READ_PARTY + UPDATE_PARTY); + } + + @Override + public <T> boolean tryUpgradeReadToWriteLock(NativeAtomicAccess<T> access, T t, long offset) { + int countWord = getCountWord(access, t, offset); + checkReadLocked(countWord); + return countWord == READ_PARTY && + casCountWord(access, t, offset, READ_PARTY, WRITE_LOCKED_COUNT_WORD); + } + + @Override + public <T> boolean tryUpgradeReadToWriteLockAndDeregisterWait( + NativeAtomicAccess<T> access, T t, long offset) { + long lockWord = getLockWord(access, t, offset); + int countWord = countWord(lockWord); + checkReadLocked(countWord); + return countWord == READ_PARTY && + tryWriteLockAndDeregisterWait0(access, t, offset, lockWord); + } + + private static <T> boolean tryWriteLockAndDeregisterWait0( + NativeAtomicAccess<T> access, T t, long offset, long lockWord) { + int waitWord = waitWord(lockWord); + checkWaitWordForDecrement(waitWord); + return casLockWord(access, t, offset, lockWord, + lockWord(WRITE_LOCKED_COUNT_WORD, waitWord - WAIT_PARTY)); + } + + @Override + public <T> boolean tryUpdateLock(NativeAtomicAccess<T> access, T t, long offset) { + long lockWord = getLockWord(access, t, offset); + int countWord = countWord(lockWord); + if (!updateLocked(countWord) && !writeLocked(countWord) && waitWord(lockWord) == 0) { + if (casCountWord(access, t, offset, countWord, countWord + UPDATE_PARTY)) + return true; + } + return false; + } + + @Override + public <T> boolean tryWriteLock(NativeAtomicAccess<T> access, T t, long offset) { + return getCountWord(access, t, offset) == 0 && + casCountWord(access, t, offset, 0, WRITE_LOCKED_COUNT_WORD); + } + + @Override + public <T> boolean tryWriteLockAndDeregisterWait( + NativeAtomicAccess<T> access, T t, long offset) { + long lockWord = getLockWord(access, t, offset); + int countWord = countWord(lockWord); + return countWord == 0 && tryWriteLockAndDeregisterWait0(access, t, offset, lockWord); + } + + @Override + public <T> void registerWait(NativeAtomicAccess<T> access, T t, long offset) { + while (true) { + int waitWord = getWaitWord(access, t, offset); + checkWaitWordForIncrement(waitWord); + if (casWaitWord(access, t, offset, waitWord, waitWord + WAIT_PARTY)) + return; + } + } + + @Override + public <T> void deregisterWait(NativeAtomicAccess<T> access, T t, long offset) { + while (true) { + int waitWord = getWaitWord(access, t, offset); + checkWaitWordForDecrement(waitWord); + if (casWaitWord(access, t, offset, waitWord, waitWord - WAIT_PARTY)) + return; + } + } + + @Override + public <T> boolean tryUpgradeUpdateToWriteLock(NativeAtomicAccess<T> access, T t, long offset) { + int countWord = getCountWord(access, t, offset); + return checkExclusiveUpdateLocked(countWord) && + casCountWord(access, t, offset, countWord, WRITE_LOCKED_COUNT_WORD); + } + + private static boolean checkExclusiveUpdateLocked(int countWord) { + checkUpdateLocked(countWord); + return countWord == UPDATE_PARTY; + } + + @Override + public <T> boolean tryUpgradeUpdateToWriteLockAndDeregisterWait( + NativeAtomicAccess<T> access, T t, long offset) { + long lockWord = getLockWord(access, t, offset); + int countWord = countWord(lockWord); + return checkExclusiveUpdateLocked(countWord) && + tryWriteLockAndDeregisterWait0(access, t, offset, lockWord); + } + + @Override + public <T> void readUnlock(NativeAtomicAccess<T> access, T t, long offset) { + while (true) { + int countWord = getCountWord(access, t, offset); + checkReadLocked(countWord); + if (casCountWord(access, t, offset, countWord, countWord - READ_PARTY)) + return; + } + } + + @Override + public <T> void updateUnlock(NativeAtomicAccess<T> access, T t, long offset) { + while (true) { + int countWord = getCountWord(access, t, offset); + checkUpdateLocked(countWord); + if (casCountWord(access, t, offset, countWord, countWord - UPDATE_PARTY)) { + return; + } + } + } + + @Override + public <T> void downgradeUpdateToReadLock(NativeAtomicAccess<T> access, T t, long offset) { + while (true) { + int countWord = getCountWord(access, t, offset); + checkUpdateLocked(countWord); + checkReadCountForIncrement(countWord); + if (casCountWord(access, t, offset, countWord, countWord - UPDATE_PARTY + READ_PARTY)) { + return; + } + } + } + + @Override + public <T> void writeUnlock(NativeAtomicAccess<T> access, T t, long offset) { + checkWriteLockedAndPut(access, t, offset, 0); + } + + private static <T> void checkWriteLockedAndPut( + NativeAtomicAccess<T> access, T t, long offset, int countWord) { + checkWriteLocked(getCountWord(access, t, offset)); + putCountWord(access, t, offset, countWord); + } + + @Override + public <T> void downgradeWriteToUpdateLock(NativeAtomicAccess<T> access, T t, long offset) { + checkWriteLockedAndPut(access, t, offset, UPDATE_PARTY); + } + + @Override + public boolean isUpdateLocked(long state) { + return updateLocked(countWord(state)); + } + + @Override + public <T> void downgradeWriteToReadLock(NativeAtomicAccess<T> access, T t, long offset) { + checkWriteLockedAndPut(access, t, offset, READ_PARTY); + } + + @Override + public <T> long getState(NativeAtomicAccess<T> access, T t, long offset) { + return getLockWord(access, t, offset); + } + + @Override + public int readLockCount(long state) { + return readCount(countWord(state)); + } + + @Override + public boolean isWriteLocked(long state) { + return writeLocked(countWord(state)); + } + + @Override + public int waitCount(long state) { + return waitWord(state); + } + + @Override + public boolean isLocked(long state) { + return countWord(state) != 0; + } + + @Override + public int lockCount(long state) { + int countWord = countWord(state); + int lockCount = readCount(countWord); + if (lockCount > 0) { + return lockCount + (updateLocked(countWord) ? 1 : 0); + + } else { + return writeLocked(countWord) ? 1 : 0; + } + } + + @Override + public String toString(long state) { + return "[read locks = " + readLockCount(state) + + ", update locked = " + isUpdateLocked(state) + + ", write locked = " + isWriteLocked(state) + + ", waits = " + waitCount(state) + "]"; + } + + @Override + public int sizeInBytes() { + return 8; + } +} diff --git a/lang/src/main/java/net/openhft/lang/locks/VanillaReadWriteWithWaitsLockingStrategy.java b/lang/src/main/java/net/openhft/lang/locks/VanillaReadWriteWithWaitsLockingStrategy.java new file mode 100644 index 0000000..37993ac --- /dev/null +++ b/lang/src/main/java/net/openhft/lang/locks/VanillaReadWriteWithWaitsLockingStrategy.java @@ -0,0 +1,232 @@ +/* + * Copyright (C) 2015 higherfrequencytrading.com + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as published by + * the Free Software Foundation, either version 3 of the License. + * + * 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 Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with this program. If not, see <http://www.gnu.org/licenses/>. + */ + +package net.openhft.lang.locks; + +public final class VanillaReadWriteWithWaitsLockingStrategy extends AbstractReadWriteLockingStrategy + implements ReadWriteWithWaitsLockingStrategy { + + private static final ReadWriteWithWaitsLockingStrategy INSTANCE = + new VanillaReadWriteWithWaitsLockingStrategy(); + + public static ReadWriteWithWaitsLockingStrategy instance() { + return INSTANCE; + } + + private VanillaReadWriteWithWaitsLockingStrategy() {} + + static final int RW_LOCK_LIMIT = 30; + static final long RW_READ_LOCKED = 1L; + static final long RW_WRITE_WAITING = 1L << RW_LOCK_LIMIT; + static final long RW_WRITE_LOCKED = 1L << 2 * RW_LOCK_LIMIT; + static final int RW_LOCK_MASK = (1 << RW_LOCK_LIMIT) - 1; + + static int rwReadLocked(long lock) { + return (int) (lock & RW_LOCK_MASK); + } + + static int rwWriteWaiting(long lock) { + return (int) ((lock >>> RW_LOCK_LIMIT) & RW_LOCK_MASK); + } + + static int rwWriteLocked(long lock) { + return (int) (lock >>> (2 * RW_LOCK_LIMIT)); + } + + static <T> long read(NativeAtomicAccess<T> access, T t, long offset) { + return access.getLongVolatile(t, offset); + } + + static <T> boolean cas(NativeAtomicAccess<T> access, T t, long offset, long expected, long x) { + return access.compareAndSwapLong(t, offset, expected, x); + } + + @Override + public <T> boolean tryReadLock(NativeAtomicAccess<T> access, T t, long offset) { + long lock = read(access, t, offset); + int writersWaiting = rwWriteWaiting(lock); + int writersLocked = rwWriteLocked(lock); + // readers wait for waiting writers + if (writersLocked <= 0 && writersWaiting <= 0) { + // increment readers locked. + int readersLocked = rwReadLocked(lock); + if (readersLocked >= RW_LOCK_MASK) + throw new IllegalMonitorStateException("readersLocked has reached a limit of " + + readersLocked); + if (cas(access, t, offset, lock, lock + RW_READ_LOCKED)) + return true; + } + return false; + } + + @Override + public <T> boolean tryWriteLock(NativeAtomicAccess<T> access, T t, long offset) { + long lock = read(access, t, offset); + int readersLocked = rwReadLocked(lock); + int writersLocked = rwWriteLocked(lock); + // writers don't wait for waiting readers. + if (readersLocked <= 0 && writersLocked <= 0) { + if (cas(access, t, offset, lock, lock + RW_WRITE_LOCKED)) + return true; + } + return false; + } + + @Override + public <T> boolean tryUpgradeReadToWriteLock(NativeAtomicAccess<T> access, T t, long offset) { + throw new UnsupportedOperationException("not implemented yet"); + } + + @Override + public <T> void readUnlock(NativeAtomicAccess<T> access, T t, long offset) { + for (; ; ) { + long lock = read(access, t, offset); + int readersLocked = rwReadLocked(lock); + if (readersLocked <= 0) + throw new IllegalMonitorStateException("readerLock underflow"); + if (cas(access, t, offset, lock, lock - RW_READ_LOCKED)) + return; + } + } + + @Override + public <T> void writeUnlock(NativeAtomicAccess<T> access, T t, long offset) { + for (; ; ) { + long lock = read(access, t, offset); + int writersLocked = rwWriteLocked(lock); + if (writersLocked != 1) + throw new IllegalMonitorStateException("writersLock underflow " + writersLocked); + if (cas(access, t, offset, lock, lock - RW_WRITE_LOCKED)) + return; + } + } + + @Override + public <T> void downgradeWriteToReadLock(NativeAtomicAccess<T> access, T t, long offset) { + throw new UnsupportedOperationException("not implemented yet"); + } + + @Override + public boolean isWriteLocked(long state) { + return rwWriteLocked(state) > 0; + } + + @Override + public int readLockCount(long state) { + return rwReadLocked(state); + } + + @Override + public <T> void reset(NativeAtomicAccess<T> access, T t, long offset) { + access.putOrderedLong(t, offset, 0L); + } + + @Override + public <T> void resetKeepingWaits(NativeAtomicAccess<T> access, T t, long offset) { + while (true) { + long lock = read(access, t, offset); + long onlyWaits = lock & ((long) RW_LOCK_MASK) << RW_LOCK_LIMIT; + if (cas(access, t, offset, lock, onlyWaits)) + return; + } + } + + @Override + public <T> void registerWait(NativeAtomicAccess<T> access, T t, long offset) { + for (; ; ) { + long lock = read(access, t, offset); + int writersWaiting = rwWriteWaiting(lock); + if (writersWaiting >= RW_LOCK_MASK) + throw new IllegalMonitorStateException("writersWaiting has reached a limit of " + + writersWaiting); + if (cas(access, t, offset, lock, lock + RW_WRITE_WAITING)) + break; + } + } + + @Override + public <T> void deregisterWait(NativeAtomicAccess<T> access, T t, long offset) { + for (; ; ) { + long lock = read(access, t, offset); + int writersWaiting = rwWriteWaiting(lock); + if (writersWaiting <= 0) + throw new IllegalMonitorStateException("writersWaiting has underflowed"); + if (cas(access, t, offset, lock, lock - RW_WRITE_WAITING)) + break; + } + } + + @Override + public <T> boolean tryWriteLockAndDeregisterWait( + NativeAtomicAccess<T> access, T t, long offset) { + long lock = read(access, t, offset); + int readersLocked = rwReadLocked(lock); + int writersWaiting = rwWriteWaiting(lock); + int writersLocked = rwWriteLocked(lock); + if (readersLocked <= 0 && writersLocked <= 0) { + // increment readers locked. + if (writersWaiting <= 0) + throw new IllegalMonitorStateException("writersWaiting has underflowed"); + // add to the readLock count and decrease the readWaiting count. + if (cas(access, t, offset, lock, lock + RW_WRITE_LOCKED - RW_WRITE_WAITING)) + return true; + } + return false; + } + + @Override + public <T> boolean tryUpgradeReadToWriteLockAndDeregisterWait( + NativeAtomicAccess<T> access, T t, long offset) { + throw new UnsupportedOperationException("not implemented yet"); + } + + @Override + public long resetState() { + return 0L; + } + + @Override + public <T> long getState(NativeAtomicAccess<T> access, T t, long offset) { + return read(access, t, offset); + } + + @Override + public int waitCount(long state) { + return rwWriteWaiting(state); + } + + @Override + public boolean isLocked(long state) { + return isReadLocked(state) || isWriteLocked(state); + } + + @Override + public int lockCount(long state) { + return rwReadLocked(state) + rwWriteLocked(state); + } + + @Override + public String toString(long state) { + return "[read locks = " + readLockCount(state) + + ", write locked = " + isWriteLocked(state) + + ", waits = " + waitCount(state) + "]"; + } + + @Override + public int sizeInBytes() { + return 8; + } +} diff --git a/lang/src/main/java/net/openhft/lang/model/Byteable.java b/lang/src/main/java/net/openhft/lang/model/Byteable.java index a6c1a54..5349319 100644 --- a/lang/src/main/java/net/openhft/lang/model/Byteable.java +++ b/lang/src/main/java/net/openhft/lang/model/Byteable.java @@ -1,17 +1,17 @@ /* - * Copyright 2013 Peter Lawrey + * Copyright (C) 2015 higherfrequencytrading.com * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as published by + * the Free Software Foundation, either version 3 of the License. * - * http://www.apache.org/licenses/LICENSE-2.0 + * 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 Lesser General Public License for more details. * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. + * You should have received a copy of the GNU Lesser General Public License + * along with this program. If not, see <http://www.gnu.org/licenses/>. */ package net.openhft.lang.model; @@ -24,11 +24,11 @@ import net.openhft.lang.io.Bytes; * Time: 21:38 */ public interface Byteable { - void bytes(Bytes bytes); - void bytes(Bytes bytes, long offset); Bytes bytes(); + long offset(); + int maxSize(); }
\ No newline at end of file diff --git a/lang/src/main/java/net/openhft/lang/model/CodeGenerator.java b/lang/src/main/java/net/openhft/lang/model/CodeGenerator.java index d1df93c..ddbba95 100644 --- a/lang/src/main/java/net/openhft/lang/model/CodeGenerator.java +++ b/lang/src/main/java/net/openhft/lang/model/CodeGenerator.java @@ -1,20 +1,20 @@ /* - * Copyright 2013 Peter Lawrey + * Copyright (C) 2015 higherfrequencytrading.com * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as published by + * the Free Software Foundation, either version 3 of the License. * - * http://www.apache.org/licenses/LICENSE-2.0 + * 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 Lesser General Public License for more details. * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. + * You should have received a copy of the GNU Lesser General Public License + * along with this program. If not, see <http://www.gnu.org/licenses/>. */ package net.openhft.lang.model; -public interface CodeGenerator { +interface CodeGenerator { } diff --git a/lang/src/main/java/net/openhft/lang/model/Copyable.java b/lang/src/main/java/net/openhft/lang/model/Copyable.java index 79baf90..8c9f2dd 100644 --- a/lang/src/main/java/net/openhft/lang/model/Copyable.java +++ b/lang/src/main/java/net/openhft/lang/model/Copyable.java @@ -1,17 +1,17 @@ /* - * Copyright 2013 Peter Lawrey + * Copyright (C) 2015 higherfrequencytrading.com * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as published by + * the Free Software Foundation, either version 3 of the License. * - * http://www.apache.org/licenses/LICENSE-2.0 + * 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 Lesser General Public License for more details. * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. + * You should have received a copy of the GNU Lesser General Public License + * along with this program. If not, see <http://www.gnu.org/licenses/>. */ package net.openhft.lang.model; diff --git a/lang/src/main/java/net/openhft/lang/model/DataValueClassCache.java b/lang/src/main/java/net/openhft/lang/model/DataValueClassCache.java new file mode 100644 index 0000000..1b15925 --- /dev/null +++ b/lang/src/main/java/net/openhft/lang/model/DataValueClassCache.java @@ -0,0 +1,50 @@ +/* + * Copyright (C) 2015 higherfrequencytrading.com + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as published by + * the Free Software Foundation, either version 3 of the License. + * + * 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 Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with this program. If not, see <http://www.gnu.org/licenses/>. + */ + +package net.openhft.lang.model; + +/** + * This is cache for the generated classes for a ClassLoader. + */ +class DataValueClassCache { + private final DataValueGenerator dvg = new DataValueGenerator(); + + public <T> T newInstance(Class<T> interfaceClass) { + try { + //noinspection ClassNewInstance + return heapClassFor(interfaceClass).newInstance(); + } catch (Exception e) { + throw new RuntimeException(e); + } + } + + public <T> T newDirectReference(Class<T> interfaceClass) { + try { + //noinspection ClassNewInstance + return directClassFor(interfaceClass).newInstance(); + } catch (Exception e) { + throw new RuntimeException(e); + } + } + + public synchronized <T> Class<T> heapClassFor(Class<T> interfaceClass) { + return dvg.acquireHeapClass(interfaceClass); + } + + public synchronized <T> Class<T> directClassFor(Class<T> interfaceClass) { + return dvg.acquireNativeClass(interfaceClass); + } +} diff --git a/lang/src/main/java/net/openhft/lang/model/DataValueClasses.java b/lang/src/main/java/net/openhft/lang/model/DataValueClasses.java new file mode 100644 index 0000000..d3ae332 --- /dev/null +++ b/lang/src/main/java/net/openhft/lang/model/DataValueClasses.java @@ -0,0 +1,68 @@ +/* + * Copyright (C) 2015 higherfrequencytrading.com + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as published by + * the Free Software Foundation, either version 3 of the License. + * + * 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 Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with this program. If not, see <http://www.gnu.org/licenses/>. + */ + +package net.openhft.lang.model; + +import net.openhft.lang.io.DirectStore; + +import java.util.WeakHashMap; + +/** + * This class is a central access point for loading generated on-heap and off heap collections. + */ +public enum DataValueClasses { + ; + // the weak hash map is required as the class loader could go away without notice e.g. in OSGi + private static final WeakHashMap<ClassLoader, DataValueClassCache> cacheMap = new WeakHashMap<ClassLoader, DataValueClassCache>(); + + public static <T> T newInstance(Class<T> interfaceClass) { + DataValueClassCache dataValueClassCache = acquireCache(interfaceClass); + return dataValueClassCache.newInstance(interfaceClass); + } + + public static <T> T newDirectReference(Class<T> interfaceClass) { + DataValueClassCache dataValueClassCache = acquireCache(interfaceClass); + return dataValueClassCache.newDirectReference(interfaceClass); + } + + public static <T> T newDirectInstance(Class<T> interfaceClass) { + T t = newDirectReference(interfaceClass); + Byteable b = (Byteable) t; + b.bytes(DirectStore.allocate(b.maxSize()).bytes(), 0); + return t; + } + + public static <T> Class<T> heapClassFor(Class<T> interfaceClass) { + DataValueClassCache dataValueClassCache = acquireCache(interfaceClass); + return dataValueClassCache.heapClassFor(interfaceClass); + } + + public static <T> Class<T> directClassFor(Class<T> interfaceClass) { + DataValueClassCache dataValueClassCache = acquireCache(interfaceClass); + return dataValueClassCache.directClassFor(interfaceClass); + } + + private static <T> DataValueClassCache acquireCache(Class<T> interfaceClass) { + ClassLoader classLoader = interfaceClass.getClassLoader(); + DataValueClassCache dataValueClassCache; + synchronized (cacheMap) { + dataValueClassCache = cacheMap.get(classLoader); + if (dataValueClassCache == null) + cacheMap.put(classLoader, dataValueClassCache = new DataValueClassCache()); + } + return dataValueClassCache; + } +} diff --git a/lang/src/main/java/net/openhft/lang/model/DataValueGenerator.java b/lang/src/main/java/net/openhft/lang/model/DataValueGenerator.java index 97ff8fe..679d318 100644..100755 --- a/lang/src/main/java/net/openhft/lang/model/DataValueGenerator.java +++ b/lang/src/main/java/net/openhft/lang/model/DataValueGenerator.java @@ -1,26 +1,29 @@ /* - * Copyright 2013 Peter Lawrey + * Copyright (C) 2015 higherfrequencytrading.com * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as published by + * the Free Software Foundation, either version 3 of the License. * - * http://www.apache.org/licenses/LICENSE-2.0 + * 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 Lesser General Public License for more details. * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. + * You should have received a copy of the GNU Lesser General Public License + * along with this program. If not, see <http://www.gnu.org/licenses/>. */ package net.openhft.lang.model; -import net.openhft.compiler.CachedCompiler; +import net.openhft.compiler.CompilerUtils; import net.openhft.lang.Compare; import net.openhft.lang.Maths; +import net.openhft.lang.MemoryUnit; import net.openhft.lang.io.Bytes; import net.openhft.lang.io.serialization.BytesMarshallable; +import net.openhft.lang.model.constraints.Group; +import org.slf4j.LoggerFactory; import java.io.IOException; import java.io.ObjectInput; @@ -28,68 +31,107 @@ import java.io.ObjectOutput; import java.lang.reflect.Method; import java.util.*; import java.util.concurrent.ConcurrentHashMap; -import java.util.logging.Logger; -/** - * User: peter.lawrey - * Date: 06/10/13 - * Time: 19:17 - */ +import static net.openhft.lang.MemoryUnit.BYTES; +import static net.openhft.lang.model.DataValueModelImpl.heapSize; + public class DataValueGenerator { - public static final Comparator<Class> COMPARATOR = new Comparator<Class>() { + private static final Comparator<Class> COMPARATOR = new Comparator<Class>() { @Override public int compare(Class o1, Class o2) { return o1.getName().compareTo(o2.getName()); } }; - public static final Comparator<Map.Entry<String, FieldModel>> COMPARE_BY_HEAP_SIZE = new Comparator<Map.Entry<String, FieldModel>>() { + private static final Comparator<Map.Entry<String, FieldModel>> COMPARE_BY_GROUP_THEN_HEAP_SIZE = new Comparator<Map.Entry<String, FieldModel>>() { @Override public int compare(Map.Entry<String, FieldModel> o1, Map.Entry<String, FieldModel> o2) { // descending - int cmp = -Maths.compare(o1.getValue().heapSize(), o2.getValue().heapSize()); - return cmp == 0 ? o1.getKey().compareTo(o2.getKey()) : cmp; + FieldModel model1 = o1.getValue(); + FieldModel model2 = o2.getValue(); + + Group group1 = model1.group(); + Group group2 = model2.group(); + + int group = Integer.compare( + group1 == null ? Integer.MIN_VALUE : model1.group().value(), + group2 == null ? Integer.MIN_VALUE : model2.group().value()); + + if (group != 0) + return group; + + int cmp = -Maths.compare(model1.heapSize(), model2.heapSize()); + if (cmp != 0) + return cmp; + Class firstPrimitiveFieldType1 = null; + if (!model1.type().isPrimitive() && + !CharSequence.class.isAssignableFrom(model1.type())) { + firstPrimitiveFieldType1 = firstPrimitiveFieldType(model1.type()); + } + Class firstPrimitiveFieldType2 = null; + if (!model2.type().isPrimitive() && + !CharSequence.class.isAssignableFrom(model2.type())) { + firstPrimitiveFieldType2 = firstPrimitiveFieldType(model2.type()); + } + if (firstPrimitiveFieldType1 != null && firstPrimitiveFieldType2 == null) + return -1; + if (firstPrimitiveFieldType1 == null && firstPrimitiveFieldType2 != null) + return 1; + if (firstPrimitiveFieldType1 != null && firstPrimitiveFieldType2 != null) { + return -Maths.compare(heapSize(firstPrimitiveFieldType1), + heapSize(firstPrimitiveFieldType2)); + } + return o1.getKey().compareTo(o2.getKey()); } }; - private static final Logger LOGGER = Logger.getLogger(DataValueGenerator.class.getName()); - final CachedCompiler cc = new CachedCompiler(null, null); private final Map<Class, Class> heapClassMap = new ConcurrentHashMap<Class, Class>(); private final Map<Class, Class> nativeClassMap = new ConcurrentHashMap<Class, Class>(); - private boolean dumpCode = false; + private boolean dumpCode = Boolean.getBoolean("dvg.dumpCode"); - private static String bytesType(Class type) { - if (type.isPrimitive()) - return Character.toUpperCase(type.getName().charAt(0)) + type.getName().substring(1); - if (CharSequence.class.isAssignableFrom(type)) - return "UTFΔ"; - return "Object"; - } - - public <T> T heapInstance(Class<T> tClass) { + public static Class firstPrimitiveFieldType(Class valueClass) { + if (valueClass.getClassLoader() == null) + return null; try { - return (T) acquireHeapClass(tClass).newInstance(); + DataValueModel valueModel; + if (valueClass.isInterface()) { + valueModel = DataValueModels.acquireModel(valueClass); + + } else { + String valueClassName = valueClass.getName(); + String $$Native = "$$Native"; + if (valueClassName.endsWith($$Native)) { + valueClassName = valueClassName.substring(0, + valueClassName.length() - $$Native.length()); + valueModel = DataValueModels.acquireModel(Class.forName(valueClassName)); + + } else { + return null; + } + } + Map.Entry<String, FieldModel>[] fields = + DataValueGenerator.heapSizeOrderedFieldsGrouped(valueModel); + if (fields.length == 0) + return null; + Class firstFieldType = fields[0].getValue().type(); + if (firstFieldType.isPrimitive()) + return firstFieldType; + return firstPrimitiveFieldType(firstFieldType); } catch (Exception e) { - throw new AssertionError(e); + return null; } } - public <T> Class acquireHeapClass(Class<T> tClass) throws ClassNotFoundException { - Class heapClass = heapClassMap.get(tClass); - if (heapClass != null) - return heapClass; - String actual = new DataValueGenerator().generateHeapObject(tClass); - if (dumpCode) - LOGGER.info(actual); - heapClass = cc.loadFromJava(tClass.getClassLoader(), tClass.getName() + "£heap", actual); - heapClassMap.put(tClass, heapClass); - return heapClass; - } - - public String generateHeapObject(Class<?> tClass) { - return generateHeapObject(DataValueModels.acquireModel(tClass)); + public static String bytesType(Class type) { + if (type.isPrimitive()) + return Character.toUpperCase(type.getName().charAt(0)) + type.getName().substring(1); + if (CharSequence.class.isAssignableFrom(type)) + return "UTFΔ"; + if (Enum.class.isAssignableFrom(type)) + return "Enum"; + return "Object"; } - public String generateHeapObject(DataValueModel<?> dvmodel) { - SortedSet<Class> imported = new TreeSet<Class>(COMPARATOR); + static String generateHeapObject(DataValueModel<?> dvmodel) { + SortedSet<Class> imported = newImported(); imported.add(BytesMarshallable.class); imported.add(Bytes.class); imported.add(IOException.class); @@ -101,53 +143,88 @@ public class DataValueGenerator { StringBuilder writeMarshal = new StringBuilder(); StringBuilder readMarshal = new StringBuilder(); StringBuilder copy = new StringBuilder(); - Map<String, ? extends FieldModel> fieldMap = dvmodel.fieldMap(); - Map.Entry<String, FieldModel>[] entries = fieldMap.entrySet().toArray(new Map.Entry[fieldMap.size()]); - Arrays.sort(entries, COMPARE_BY_HEAP_SIZE); + Map.Entry<String, FieldModel>[] entries = heapSizeOrderedFieldsGrouped(dvmodel); for (Map.Entry<String, ? extends FieldModel> entry : entries) { String name = entry.getKey(); FieldModel model = entry.getValue(); Class type = model.type(); - if (!type.isPrimitive() && !type.getPackage().getName().equals("java.lang")) + if (shouldImport(type)) imported.add(type); - fieldDeclarations.append(" private ").append(type.getName()).append(" _").append(name).append(";\n"); + heapFieldDeclarations(fieldDeclarations, type, name, model); + + Method setter = getSetter(model); + Method getter = getGetter(model); + Method getUsing = getUsing(model); + + boolean bothVolatileAndPlain = false; + + final Method orderedSetter = getOrderedSetter(model); + final Method volatileGetter = getVolatileGetter(model); + + if (getter != null && volatileGetter != null) { + bothVolatileAndPlain = true; + } + if (setter == null && orderedSetter != null) { + setter = orderedSetter; + } + if (getter == null && volatileGetter != null) { + getter = volatileGetter; + } - final Method setter = model.setter(); - final Method getter = model.getter(); if (setter == null) { - copy.append(" ((Copyable) ").append(getter.getName()).append("()).copyFrom(from.").append(getter.getName()).append("());\n"); + if (getter != null) + copy.append(" ((Copyable) ").append(getter.getName()).append("()).copyFrom(from.").append(getter.getName()).append("());\n"); + } else { - copy.append(" ").append(setter.getName()).append("(from.").append(getter.getName()).append("());\n"); - Class<?> setterType = setter.getParameterTypes()[0]; - getterSetters.append(" public void ").append(setter.getName()).append('(').append(setterType.getName()).append(" _) {\n"); - if (type == String.class && setterType != String.class) - getterSetters.append(" _").append(name).append(" = _.toString();\n"); - else - getterSetters.append(" _").append(name).append(" = _;\n"); - getterSetters.append(" }\n\n"); + methodCopy(copy, getter, setter, model); + methodHeapSet(getterSetters, setter, name, type, model); } - getterSetters.append(" public ").append(type.getName()).append(' ').append(getter.getName()).append("() {\n"); - getterSetters.append(" return _").append(name).append(";\n"); - getterSetters.append(" }\n\n"); + if (getter != null) + methodHeapGet(getterSetters, getter, name, type, model); + + if (getUsing != null && type == String.class && !model.isArray()) { + methodHeapGetUsingWithStringBuilder(getterSetters, getUsing, name, type, model); + + // we have to add in the getter method as its required for the equals() and hashCode() + if (getter == null && volatileGetter == null) { + String getterName = getterName(getUsing); + methodHeapGet(getterSetters, name, type, getterName); + } + } + + //In the case where there are both volatile and plain gets and sets they need to be written here + //If there is just a volatile get and set it would have been written above. + if (bothVolatileAndPlain) { + methodHeapGet(getterSetters, volatileGetter, name, type, model); + methodHeapSet(getterSetters, orderedSetter, name, type, model); + } + Method adder = model.adder(); if (adder != null) { - getterSetters.append(" public ").append(type.getName()).append(' ').append(adder.getName()) - .append("(").append(adder.getParameterTypes()[0].getName()).append(" _) {\n") - .append(" return _").append(name).append(" += _;\n") + getterSetters.append(" public ").append(normalize(type)).append(' ').append(adder.getName()) + .append("(").append(adder.getParameterTypes()[0].getName()).append(" $) {\n") + .append(" return _").append(name).append(" += $;\n") .append(" }"); } + Method sizeOf = model.sizeOf(); + if (sizeOf != null) { + getterSetters.append(" public int ").append(sizeOf.getName()) + .append("() {\n") + .append(" return ").append(model.indexSize().value()).append(";\n") + .append(" }\n\n"); + } Method atomicAdder = model.atomicAdder(); if (atomicAdder != null) { - getterSetters.append(" public synchronized ").append(type.getName()).append(' ').append(atomicAdder.getName()) - .append("(").append(adder.getParameterTypes()[0].getName()).append(" _) {\n") - .append(" return _").append(name).append(" += _;\n") - .append(" }"); + getterSetters.append(" public synchronized ").append(normalize(type)).append(' ').append(atomicAdder.getName()) + .append("(").append(atomicAdder.getParameterTypes()[0].getName()).append(" $) {\n") + .append(" return _").append(name).append(" += $;\n") + .append(" }\n\n"); } Method cas = model.cas(); if (cas != null) { getterSetters.append(" public synchronized boolean ").append(cas.getName()).append("(") - .append(type.getName()).append(" _1, ") - .append(type.getName()).append(" _2) {\n") + .append(normalize(type)).append(" _1, ") + .append(normalize(type)).append(" _2) {\n") .append(" if (_").append(name).append(" == _1) {\n") .append(" _").append(name).append(" = _2;\n") .append(" return true;\n") @@ -179,25 +256,23 @@ public class DataValueGenerator { .append(" throw new UnsupportedOperationException();\n") .append(" }"); } - writeMarshal.append(" out.write").append(bytesType(type)).append("(_").append(name).append(");\n"); - readMarshal.append(" _").append(name).append(" = in.read").append(bytesType(type)).append("("); - if ("Object".equals(bytesType(type))) - readMarshal.append(type.getName()).append(".class"); - readMarshal.append(");\n"); + methodWriteMarshall(writeMarshal, getter, setter, type, model); + methodHeapReadMarshall(readMarshal, name, type, model); } StringBuilder sb = new StringBuilder(); - sb.append("package ").append(dvmodel.type().getPackage().getName()).append(";\n\n"); + appendPackage(dvmodel, sb); sb.append("import static ").append(Compare.class.getName()).append(".*;\n"); for (Class aClass : imported) { - sb.append("import ").append(aClass.getName()).append(";\n"); + sb.append("import ").append(normalize(aClass)).append(";\n"); } - sb.append("\npublic class ").append(dvmodel.type().getSimpleName()) - .append("£heap implements ").append(dvmodel.type().getSimpleName()) - .append(", BytesMarshallable, Copyable<").append(dvmodel.type().getName()).append("> {\n"); + String className = simpleName(dvmodel.type()); + sb.append("\npublic class ").append(className) + .append("$$Heap implements ").append(dvmodel.type().getSimpleName()) + .append(", BytesMarshallable, Copyable<").append(normalize(dvmodel.type())).append("> {\n"); sb.append(fieldDeclarations).append('\n'); sb.append(getterSetters); - sb.append(" @SuppressWarnings(\"unchecked\")\n" + - " public void copyFrom(").append(dvmodel.type().getName()).append(" from) {\n"); + sb.append(" @SuppressWarnings(\"unchecked\")\n" + + " public void copyFrom(").append(normalize(dvmodel.type())).append(" from) {\n"); sb.append(copy); sb.append(" }\n\n"); sb.append(" public void writeMarshallable(Bytes out) {\n"); @@ -207,41 +282,80 @@ public class DataValueGenerator { sb.append(readMarshal); sb.append(" }\n"); if (Byteable.class.isAssignableFrom(dvmodel.type())) { - sb.append(" public void bytes(Bytes bytes) {\n"); - sb.append(" throw new UnsupportedOperationException();\n"); - sb.append(" }\n"); sb.append(" public void bytes(Bytes bytes, long l) {\n"); sb.append(" throw new UnsupportedOperationException();\n"); sb.append(" }\n"); sb.append(" public Bytes bytes() {\n"); sb.append(" return null;\n"); sb.append(" }\n"); + sb.append(" public long offset() {\n"); + sb.append(" return 0L;\n"); + sb.append(" }\n"); sb.append(" public int maxSize() {\n"); sb.append(" throw new UnsupportedOperationException();\n"); sb.append(" }\n"); } - generateObjectMethods(sb, dvmodel, entries); + generateObjectMethods(sb, dvmodel, entries, false); sb.append("}\n"); // System.out.println(sb); return sb.toString(); } - private static void generateObjectMethods(StringBuilder sb, DataValueModel<?> dvmodel, Map.Entry<String, FieldModel>[] entries) { + public static String simpleName(Class<?> type) { + String name = type.getName(); + return name.substring(name.lastIndexOf('.') + 1); + } + + public static CharSequence normalize(Class aClass) { + return aClass.getName().replace('$', '.'); + } + + private static void generateObjectMethods(StringBuilder sb, DataValueModel<?> dvmodel, + Map.Entry<String, FieldModel>[] entries, + boolean offHeap) { int count = 0; StringBuilder hashCode = new StringBuilder(); StringBuilder equals = new StringBuilder(); + StringBuilder equalsGetUsing = new StringBuilder(); + StringBuilder toStringGetUsing = new StringBuilder(); + StringBuilder getUsingEquals = new StringBuilder(); StringBuilder toString = new StringBuilder(); for (Map.Entry<String, FieldModel> entry : entries) { String name = entry.getKey(); FieldModel model = entry.getValue(); - Class type = model.type(); - if (count > 0) - hashCode.append(") * 10191 +\n "); - String getterName = model.getter().getName(); - hashCode.append("calcLongHashCode(").append(getterName).append("())"); - equals.append(" if(!isEqual(").append(getterName).append("(), that.").append(getterName).append("())) return false;\n"); - toString.append(" \", ").append(name).append("= \" + ").append(getterName).append("() +\n"); - count++; + Method getter = getGetter(model); + Method getUsing = getUsing(model); + + if (getter == null) getter = getVolatileGetter(model); + + if (getter != null || getUsing != null) { + String getterName = (getter == null) ? getterName(getUsing) : getter.getName(); + methodLongHashCode(hashCode, getterName, model, count); + + if (getter != null) { + methodEquals(equals, getterName, model, simpleName(dvmodel.type())); + methodToString(toString, getterName, name, model); + + } else { + methodEqualsGetUsing(getUsingEquals, getUsing.getName()); + methodToStringGetUsing(toStringGetUsing, getUsing.getName(), name, model); + } + count++; + } + + Bytes b; + + if (model.isArray()) { + String nameWithUpper = Character.toUpperCase(name.charAt(0)) + name.substring(1); + if (model.isVolatile()) nameWithUpper = "Volatile" + nameWithUpper; + sb.append("\n public long longHashCode_" + name + "() {\n" + + " long hc = 0;\n" + + " for (int i = 0; i < " + model.indexSize().value() + "; i++) {\n" + + " hc += calcLongHashCode(get" + nameWithUpper + "At(i));\n" + + " }\n" + + " return hc;\n" + + " }\n\n"); + } } sb.append(" public int hashCode() {\n" + " long lhc = longHashCode();\n" + @@ -252,48 +366,465 @@ public class DataValueGenerator { " return "); for (int i = 1; i < count; i++) sb.append('('); + sb.append(hashCode); - String simpleName = dvmodel.type().getSimpleName(); + + CharSequence simpleName = simpleName(dvmodel.type()).replace('$', '.'); sb.append(";\n") .append(" }\n") - .append("\n") - .append(" public boolean equals(Object o) {\n") + .append("\n"); + sb.append(" public boolean equals(Object o) {\n") .append(" if (this == o) return true;\n") .append(" if (!(o instanceof ").append(simpleName).append(")) return false;\n") .append(" ").append(simpleName).append(" that = (").append(simpleName).append(") o;\n") .append("\n") .append(equals) - .append(" return true;\n" + - " }\n" + - "\n" + - " public String toString() {\n" + - " return \"").append(simpleName).append(" {\" +\n") - .append(toString.substring(0, toString.length() - 3)).append(";\n") - .append(" }"); + .append(equalsGetUsing) + .append(" return true;\n") + .append(" }\n") + .append("\n"); + sb.append(" public String toString() {\n") + .append(offHeap ? " if (_bytes == null) return \"bytes is null\";\n" : "") + .append(" StringBuilder sb = new StringBuilder();\n") + .append(" sb.append(\"").append(simpleName).append("{ \");\n") + .append(toString) + .append(toStringGetUsing) + .append(" sb.append(\" }\");\n") + .append(" return sb.toString();\n") + .append(" }\n"); + } + + private static Method getUsing(FieldModel model) { + Method getUsing = model.getUsing(); + return getUsing; + } + + public static Method getGetter(FieldModel model) { + Method getter = model.getter(); + if (getter == null) getter = model.indexedGetter(); + return getter; + } + + public static Method getVolatileGetter(FieldModel model) { + Method getter = model.volatileGetter(); + if (getter == null) getter = model.volatileIndexedGetter(); + return getter; + } + + public static Method getSetter(FieldModel model) { + Method setter = model.setter(); + if (setter == null) setter = model.indexedSetter(); + + return setter; + } + + public static Method getOrderedSetter(FieldModel model) { + Method setter = model.orderedSetter(); + if (setter == null) setter = model.orderedIndexedSetter(); + + return setter; + } + + private static void methodCopy( + StringBuilder copy, Method getter, Method setter, FieldModel model) { + if (!model.isArray()) { + if (model.setter() != null && getter != null) { + copy.append(" ").append(setter.getName()); + copy.append("(from.").append(getter.getName()).append("());\n"); + } + } else { + copy.append(" for (int i = 0; i < ").append(model.indexSize().value()) + .append("; i++){"); + copy.append("\n ").append(setter.getName()).append("(i, from.") + .append(getter.getName()).append("(i));\n"); + copy.append(" }\n"); + } + } + + private static void methodWriteMarshall(StringBuilder writeMarshal, Method getter, + Method setter, Class type, FieldModel model) { + if (!model.isArray()) { + if (getter != null && setter != null) { + writeMarshal.append(" {\n"); +// saveCharSequencePosition(writeMarshal, type, "out"); + writeMarshal.append(" out.write").append(bytesType(type)).append("(") + .append(getter.getName()).append("());\n"); +// zeroOutRemainingCharSequenceBytesAndUpdatePosition( +// writeMarshal, model, type, "out"); + writeMarshal.append(" }\n"); + } + // otherwise skip. + } else { + writeMarshal.append(" for (int i = 0; i < ") + .append(model.indexSize().value()).append("; i++){\n"); +// saveCharSequencePosition(writeMarshal, type, "out"); + writeMarshal.append(" out.write").append(bytesType(type)).append("(") + .append(getter.getName()).append("(i));\n"); +// zeroOutRemainingCharSequenceBytesAndUpdatePosition( +// writeMarshal, model, type, "out"); + writeMarshal.append(" }\n"); + } + } + + private static void saveCharSequencePosition(StringBuilder write, Class type, String bytes) { + if (CharSequence.class.isAssignableFrom(type)) + write.append(" long pos = " + bytes + ".position();\n"); + } + + private static void zeroOutRemainingCharSequenceBytesAndUpdatePosition( + StringBuilder write, FieldModel model, Class type, String bytes) { + if (CharSequence.class.isAssignableFrom(type)) { + write.append(" long newPos = pos + ").append(fieldSize(model)) + .append(";\n"); + write.append(" " + bytes + ".zeroOut(" + bytes + ".position(), newPos);\n"); + write.append(" " + bytes + ".position(newPos);\n"); + } + } + + private static void methodHeapReadMarshall(StringBuilder readMarshal, String name, Class type, FieldModel model) { + + if(model.type() == Date.class){ + readMarshal.append(" _").append(name).append(" = new Date(in.readLong());\n"); + } else { + String str = bytesType(type); + if (!model.isArray()) { + readMarshal.append(" _").append(name).append(" = in.read").append(str).append("("); + if ("Object".equals(str) || "Enum".equals(str)) + readMarshal.append(normalize(type)).append(".class"); + readMarshal.append(");\n"); + + } else { + readMarshal.append(" for (int i = 0; i < ").append(model.indexSize().value()).append("; i++){\n"); + readMarshal.append(" _").append(name).append("[i] = in.read").append(str).append("("); + if ("Object".equals(str) || "Enum".equals(str)) + readMarshal.append(normalize(type)).append(".class"); + readMarshal.append(");\n"); + readMarshal.append(" }\n"); + } + } + } + + private static void methodLongHashCode(StringBuilder hashCode, String getterName, FieldModel model, int count) { + if (count > 0) + hashCode.append(") * 10191 +\n "); + + if (!model.isArray()) { + hashCode.append("calcLongHashCode(").append(getterName).append("())"); + + } else { + hashCode.append("longHashCode_").append(model.name()).append("()"); + } + } + + private static void methodEqualsGetUsing(StringBuilder equals, String getterName) { + equals.append(" if(!isEqual(").append(getterName).append("(new StringBuilder()).toString(), that.").append(getterName).append("new StringBuilder().toString())) return false;\n"); + } + + private static void methodEquals(StringBuilder equals, String getterName, FieldModel model, String className) { + if (!model.isArray()) { + equals.append(" if(!isEqual(").append(getterName).append("(), that.").append(getterName).append("())) return false;\n"); + + } else { + equals.append(" for (int i = 0; i <" + model.indexSize().value() + "; i++) {\n"); + equals.append(" if(!isEqual(").append(getterName).append("(i), that.").append(getterName).append("(i))) return false;\n"); + equals.append(" }\n"); + } + } + + private static void methodToStringGetUsing(StringBuilder toString, String getterName, String name, FieldModel model) { + toString.append(" sb.append(\"").append(name).append("= \").append(").append(getterName).append("(new StringBuilder()));\n"); + } + + private static void methodToString(StringBuilder toString, String getterName, String name, FieldModel model) { + if (toString.length() > 2) + toString.append("sb.append(\", \")\n;"); + if (!model.isArray()) { + toString.append(" sb.append(\"").append(name).append("= \").append(").append(getterName).append("());\n"); + + } else { + toString + .append(" sb.append(\"").append(name).append("\").append(\"= [\");") + .append(" for (int i = 0; i < ").append(model.indexSize().value()).append("; i++) {\n") + .append(" if (i > 0) sb.append(\", \") ;\n") + .append(" sb.append(").append(getterName).append("(i));\n") + .append(" }\n") + .append(" sb.append(\"]\");\n"); + } + } + + private static String nullAwareToString(String var) { + return "(" + var + " != null ? " + var + ".toString() : null)"; + } + + private static void methodHeapSet(StringBuilder getterSetters, Method setter, String name, Class type, FieldModel model) { + Class<?> setterType = setter.getParameterTypes()[setter.getParameterTypes().length - 1]; + if (!model.isArray()) { + getterSetters.append(" public void ").append(setter.getName()).append('(').append(normalize(setterType)).append(" $) {\n"); + if (type == String.class && setterType != String.class) + getterSetters.append(" _").append(name).append(" = " + nullAwareToString("$") + ";\n"); + else + getterSetters.append(" _").append(name).append(" = $;\n"); + + } else { + getterSetters.append(" public void ").append(setter.getName()).append("(int i, ").append(normalize(setterType)).append(" $) {\n"); + getterSetters.append(boundsCheck(model.indexSize().value())); + if (type == String.class && setterType != String.class) + getterSetters.append(" _").append(name).append("[i] = " + nullAwareToString("$") + ";\n"); + else + getterSetters.append(" _").append(name).append("[i] = $;\n"); + } + getterSetters.append(" }\n\n"); + } + + private static void methodHeapGet(StringBuilder getterSetters, Method getter, String name, Class type, FieldModel model) { + if (!model.isArray()) { + getterSetters.append(" public ").append(normalize(type)).append(' ').append(getter.getName()).append("() {\n"); + getterSetters.append(" return _").append(name).append(";\n"); + + } else { + getterSetters.append(" public ").append(normalize(type)).append(' ').append(getter.getName()).append("(int i) {\n"); + getterSetters.append(boundsCheck(model.indexSize().value())); + getterSetters.append(" return _").append(name).append("[i];\n"); + } + getterSetters.append(" }\n\n"); + } + + private static void methodHeapGet(StringBuilder getterSetters, String name, Class type, String getterName) { + getterSetters.append(" public ").append(normalize(type)).append(' ').append(getterName).append("() {\n"); + getterSetters.append(" return _").append(name).append(";\n"); + getterSetters.append(" }\n\n"); + } + + private static void methodHeapGetUsingWithStringBuilder(StringBuilder result, Method method, String name, Class type, FieldModel model) { + final CharSequence returnType = method.getReturnType() == void.class ? "void" : normalize(method + .getReturnType()); + + if (!type.equals(String.class) || method.getParameterTypes().length != 1) + return; + + if (!StringBuilder.class.equals(method.getParameterTypes()[0])) + return; + + result.append(" public ").append(returnType).append(' ').append(method + .getName()) + .append("(StringBuilder builder){\n"); + result.append(" builder.append(_" + name + ");\n"); + if (method.getReturnType() != void.class) + result.append(" return builder;\n"); + + result.append(" }\n\n"); + } + + private static void heapFieldDeclarations(StringBuilder fieldDeclarations, Class type, String name, FieldModel model) { + String vol = ""; + if (model.isVolatile()) vol = "volatile "; + + if (!model.isArray()) { + fieldDeclarations.append(" private ").append(vol).append(normalize(type)).append(" _").append(name).append(";\n"); + + } else { + fieldDeclarations.append(" private ").append(vol).append(normalize(type)).append("[] _").append(name) + .append(" = new ").append(normalize(type)).append("[").append(model.indexSize().value()).append("];\n"); + if (!type.isPrimitive()) { + fieldDeclarations.append(" {\n") + .append(" for (int i = 0; i < _").append(name).append(".length; i++)\n") + .append(" _").append(name).append("[i] = new ").append(type.getName()); + + if (type.isInterface()) { + fieldDeclarations.append("$$Heap();\n"); + + } else { + fieldDeclarations.append("();\n"); + } + fieldDeclarations.append(" }"); + } + } + } + + private static String boundsCheck(int check) { + return " if(i<0) throw new ArrayIndexOutOfBoundsException(i + \" must be greater than 0\");\n" + + " if(i>=" + check + ") throw new ArrayIndexOutOfBoundsException(i + \" must be less than " + check + "\");\n"; + } + + public static void appendImported(SortedSet<Class> imported, StringBuilder sb) { + for (Class aClass : imported) { + sb.append("import ").append(aClass.getName().replace('$', '.')).append(";\n"); + } + } + + public static void appendPackage(DataValueModel<?> dvmodel, StringBuilder sb) { + sb.append("package ").append(getPackage(dvmodel)).append(";\n\n"); + } + + public static String getPackage(DataValueModel<?> dvmodel) { + return dvmodel.type().getPackage().getName(); + } + + public static int fieldSize(FieldModel model) { + return computeOffset( + (int) BYTES.alignAndConvert((long) model.nativeSize(), MemoryUnit.BITS), model); + } + + public static TreeSet<Class> newImported() { + return new TreeSet<Class>(COMPARATOR); + } + + public static boolean shouldImport(Class type) { + return !type.isPrimitive() && !type.getPackage().getName().equals("java.lang"); + } + + public static Map.Entry<String, FieldModel>[] heapSizeOrderedFieldsGrouped(DataValueModel<?> dvmodel) { + Map<String, ? extends FieldModel> fieldMap = dvmodel.fieldMap(); + Map.Entry<String, FieldModel>[] entries = + fieldMap.entrySet().toArray(new Map.Entry[fieldMap.size()]); + Arrays.sort(entries, COMPARE_BY_GROUP_THEN_HEAP_SIZE); + return entries; + } + + /** + * gets the getter name based on the getUsing + */ + private static String getterName(Method getUsingMethod) { + String name = getUsingMethod.getName(); + if (!name.startsWith("getUsing")) + throw new IllegalArgumentException("expected the getUsingXX method to start with the text 'getUsing'."); + return "get" + name.substring("getUsing".length()); + } + + private static void methodGetUsingWithStringBuilder(StringBuilder result, Method method, Class type, boolean isVolatile, String name) { + String read = "read"; + if (isVolatile) read = "readVolatile"; + + if (method.getParameterTypes().length != 1) + return; + + if (!StringBuilder.class.equals(method.getParameterTypes()[0])) + return; + + if (type != String.class) + return; + + final CharSequence returnType = method.getReturnType() == void.class ? "void" : normalize(method + .getReturnType()); + + result.append(" public ").append(returnType).append(' ').append(method + .getName()) + .append("(StringBuilder builder){\n"); + + result.append(" _bytes.position(_offset + ").append(name.toUpperCase()).append(");\n"); + result.append(" _bytes.").append(read).append(bytesType(type)).append("(builder);\n"); + + if (method.getReturnType() != void.class) { + result.append(" return builder;\n"); + } + result.append(" }\n\n"); + } + + public static int computeOffset(int offset, FieldModel model) { + if (model.indexSize() == null) { + return offset; + + } else { + return model.indexSize().value() * offset; + } + } + + public static int computeNonScalarOffset(DataValueModel dvmodel, Class type) { + int offset = 0; + DataValueModel dvmodel2 = dvmodel.nestedModel(type); + Map.Entry<String, FieldModel>[] entries2 = heapSizeOrderedFieldsGrouped(dvmodel2); + for (Map.Entry<String, ? extends FieldModel> entry2 : entries2) { + FieldModel model2 = entry2.getValue(); + int add; + if (dvmodel2.isScalar(model2.type())) { + add = fieldSize(model2); + + } else { + add = computeNonScalarOffset(dvmodel2, model2.type()); + if (model2.isArray()) + add *= model2.indexSize().value(); + } + offset += add; + } + return offset; + } + + public <T> T heapInstance(Class<T> tClass) { + try { + //noinspection ClassNewInstance + return (T) acquireHeapClass(tClass).newInstance(); + } catch (Exception e) { + throw new AssertionError(e); + } + } + + public <T> Class acquireHeapClass(Class<T> tClass) { + Class heapClass = heapClassMap.get(tClass); + if (heapClass != null) + return heapClass; + ClassLoader classLoader = tClass.getClassLoader(); + String className = tClass.getName() + "$$Heap"; + try { + heapClass = classLoader.loadClass(className); + } catch (ClassNotFoundException ignored) { + try { + String actual = generateHeapObject(tClass); + if (dumpCode) + LoggerFactory.getLogger(DataValueGenerator.class).info(actual); + heapClass = CompilerUtils.CACHED_COMPILER.loadFromJava(classLoader, className, actual); + } catch (ClassNotFoundException e) { + throw new AssertionError(e); + } + } + heapClassMap.put(tClass, heapClass); + return heapClass; + } + + String generateHeapObject(Class<?> tClass) { + DataValueModel<?> dvmodel = DataValueModels.acquireModel(tClass); + for (FieldModel fieldModel : dvmodel.fieldMap().values()) { + if (fieldModel.isArray() && !fieldModel.type().isPrimitive()) + acquireHeapClass(fieldModel.type()); + } + return generateHeapObject(dvmodel); } public <T> T nativeInstance(Class<T> tClass) { try { + //noinspection ClassNewInstance return (T) acquireNativeClass(tClass).newInstance(); } catch (Exception e) { throw new AssertionError(e); } } - public <T> Class acquireNativeClass(Class<T> tClass) throws ClassNotFoundException { + public <T> Class acquireNativeClass(Class<T> tClass) { + if (!tClass.isInterface()) + return tClass; Class nativeClass = nativeClassMap.get(tClass); if (nativeClass != null) return nativeClass; DataValueModel<T> dvmodel = DataValueModels.acquireModel(tClass); for (Class clazz : dvmodel.nestedModels()) { + // touch them to make sure they are loaded. Class clazz2 = acquireNativeClass(clazz); } String actual = new DataValueGenerator().generateNativeObject(dvmodel); if (dumpCode) - LOGGER.info(actual); - nativeClass = cc.loadFromJava(tClass.getClassLoader(), tClass.getName() + "£native", actual); + LoggerFactory.getLogger(DataValueGenerator.class).info(actual); + ClassLoader classLoader = tClass.getClassLoader(); + String className = tClass.getName() + "$$Native"; + try { + nativeClass = classLoader.loadClass(className); + } catch (ClassNotFoundException ignored) { + try { + nativeClass = CompilerUtils.CACHED_COMPILER.loadFromJava(classLoader, className, actual); + } catch (ClassNotFoundException e) { + throw new AssertionError(e); + } + } nativeClassMap.put(tClass, nativeClass); return nativeClass; } @@ -303,7 +834,7 @@ public class DataValueGenerator { } public String generateNativeObject(DataValueModel<?> dvmodel) { - SortedSet<Class> imported = new TreeSet<Class>(COMPARATOR); + SortedSet<Class> imported = newImported(); imported.add(BytesMarshallable.class); imported.add(ObjectOutput.class); imported.add(ObjectInput.class); @@ -319,51 +850,73 @@ public class DataValueGenerator { StringBuilder readMarshal = new StringBuilder(); StringBuilder copy = new StringBuilder(); StringBuilder nestedBytes = new StringBuilder(); - Map<String, ? extends FieldModel> fieldMap = dvmodel.fieldMap(); - Map.Entry<String, FieldModel>[] entries = fieldMap.entrySet().toArray(new Map.Entry[fieldMap.size()]); - Arrays.sort(entries, COMPARE_BY_HEAP_SIZE); + + Map.Entry<String, FieldModel>[] entries = heapSizeOrderedFieldsGrouped(dvmodel); int offset = 0; for (Map.Entry<String, ? extends FieldModel> entry : entries) { String name = entry.getKey(); FieldModel model = entry.getValue(); Class type = model.type(); - if (!type.isPrimitive() && !type.getPackage().getName().equals("java.lang")) + if (shouldImport(type)) imported.add(type); String NAME = "_offset + " + name.toUpperCase(); - final Method setter = model.setter(); - final Method getter = model.getter(); + final Method setter = getSetter(model); + final Method getter = getGetter(model); + final Method getUsing = getUsing(model); + + final Method orderedSetter = getOrderedSetter(model); + final Method volatileGetter = getVolatileGetter(model); + + final Method defaultSetter = setter != null ? setter : orderedSetter; + final Method defaultGetter = getter != null ? getter : volatileGetter; + if (dvmodel.isScalar(type)) { staticFieldDeclarations.append(" private static final int ").append(name.toUpperCase()).append(" = ").append(offset).append(";\n"); - copy.append(" ").append(setter.getName()).append("(from.").append(getter.getName()).append("());\n"); - Class<?> setterType = setter.getParameterTypes()[0]; - getterSetters.append(" public void ").append(setter.getName()).append('(').append(setterType.getName()).append(" _) {\n"); - getterSetters.append(" _bytes.write").append(bytesType(type)).append("(").append(NAME).append(", "); - if (CharSequence.class.isAssignableFrom(type)) - getterSetters.append(model.size().value()).append(", "); - getterSetters.append("_);\n"); - getterSetters.append(" }\n\n"); - getterSetters.append(" public ").append(type.getName()).append(' ').append(getter.getName()).append("() {\n"); - getterSetters.append(" return _bytes.read").append(bytesType(type)).append("(").append(NAME).append(");\n"); - getterSetters.append(" }\n\n"); + methodCopy(copy, defaultGetter, defaultSetter, model); + if (setter != null) + methodSet(getterSetters, setter, type, NAME, model, false); + if (getter != null) + methodGet(getterSetters, getter, type, NAME, model, false); + if (getUsing != null) { + methodGetUsingWithStringBuilder(getterSetters, getUsing, type, false, name); + + // we have to add in the getter method as its required for the equals() and hashCode() + if (getter == null && volatileGetter == null) { + String getterName = getterName(getUsing); + methodGet(getterSetters, type, NAME, false, getterName); + } + } + + if (orderedSetter != null) + methodSet(getterSetters, orderedSetter, type, NAME, model, true); + if (volatileGetter != null) + methodGet(getterSetters, volatileGetter, type, NAME, model, true); + Method adder = model.adder(); if (adder != null) { - getterSetters.append(" public ").append(type.getName()).append(' ').append(adder.getName()) - .append("(").append(adder.getParameterTypes()[0].getName()).append(" _) {\n") - .append(" return _bytes.add").append(bytesType(type)).append("(").append(NAME).append(", _);\n") + getterSetters.append(" public ").append(normalize(type)).append(' ').append(adder.getName()) + .append("(").append(adder.getParameterTypes()[0].getName()).append(" $) {\n") + .append(" return _bytes.add").append(bytesType(type)).append("(").append(NAME).append(", $);\n") .append(" }"); } Method atomicAdder = model.atomicAdder(); if (atomicAdder != null) { - getterSetters.append(" public ").append(type.getName()).append(' ').append(atomicAdder.getName()) - .append("(").append(adder.getParameterTypes()[0].getName()).append(" _) {\n") - .append(" return _bytes.addAtomic").append(bytesType(type)).append("(").append(NAME).append(", _);\n") + getterSetters.append(" public ").append(normalize(type)).append(' ').append(atomicAdder.getName()) + .append("(").append(atomicAdder.getParameterTypes()[0].getName()).append(" $) {\n") + .append(" return _bytes.addAtomic").append(bytesType(type)).append("(").append(NAME).append(", $);\n") .append(" }"); } + Method sizeOf = model.sizeOf(); + if (sizeOf != null) { + getterSetters.append(" public int ").append(sizeOf.getName()) + .append("() {\n").append(" return ").append(model.indexSize().value()).append(";\n") + .append(" }\n\n"); + } Method cas = model.cas(); if (cas != null) { getterSetters.append(" public boolean ").append(cas.getName()).append("(") - .append(type.getName()).append(" _1, ") - .append(type.getName()).append(" _2) {\n") + .append(normalize(type)).append(" _1, ") + .append(normalize(type)).append(" _2) {\n") .append(" return _bytes.compareAndSwap").append(bytesType(type)).append('(').append(NAME).append(", _1, _2);\n") .append(" }"); } @@ -391,83 +944,77 @@ public class DataValueGenerator { .append(" _bytes.busyLock").append(bytesType(type)).append('(').append(NAME).append(");\n") .append(" }"); } - writeMarshal.append(" out.write").append(bytesType(type)).append("(") - .append(getter.getName()).append("());\n"); - readMarshal.append(" ").append(setter.getName()).append("(in.read").append(bytesType(type)).append("());\n"); - offset += (model.nativeSize() + 7) >> 3; + methodWriteMarshall(writeMarshal, defaultGetter, defaultSetter, type, model); + methodReadMarshall(readMarshal, defaultGetter, defaultSetter, type, model); + + if(!Enum.class.isAssignableFrom(type)) + offset += fieldSize(model); + } else { staticFieldDeclarations.append(" private static final int ").append(name.toUpperCase()).append(" = ").append(offset).append(";\n"); - fieldDeclarations.append(" private final ").append(type.getName()).append("£native _").append(name).append(" = new ").append(type.getName()).append("£native();\n"); - if (setter == null) { + nonScalarFieldDeclaration(staticFieldDeclarations, type, name, model); + if (defaultSetter == null) { copy.append(" _").append(name).append(".copyFrom(from.").append(getter.getName()).append("());\n"); + } else { - copy.append(" ").append(setter.getName()).append("(from.").append(getter.getName()).append("());\n"); - Class<?> setterType = setter.getParameterTypes()[0]; - getterSetters.append(" public void ").append(setter.getName()).append('(').append(setterType.getName()).append(" _) {\n"); - if (type == String.class && setterType != String.class) - getterSetters.append(" _").append(name).append(" = _.toString();\n"); - else - getterSetters.append(" _").append(name).append(".copyFrom(_);\n"); - getterSetters.append(" }\n\n"); + methodCopy(copy, defaultGetter, defaultSetter, model); + methodNonScalarSet(getterSetters, defaultSetter, name, type, model); } - getterSetters.append(" public ").append(type.getName()).append(' ').append(getter.getName()).append("() {\n"); - getterSetters.append(" return _").append(name).append(";\n"); - getterSetters.append(" }\n\n"); - - writeMarshal.append(" _").append(name).append(".writeMarshallable(out);\n"); - readMarshal.append(" _").append(name).append(".readMarshallable(in);\n"); - - nestedBytes.append(" ((Byteable) _").append(name).append(").bytes(bytes, ").append(NAME).append(");\n"); - DataValueModel dvmodel2 = dvmodel.nestedModel(type); - Map<String, ? extends FieldModel> fieldMap2 = dvmodel2.fieldMap(); - Map.Entry<String, FieldModel>[] entries2 = fieldMap2.entrySet().toArray(new Map.Entry[fieldMap2.size()]); - Arrays.sort(entries2, COMPARE_BY_HEAP_SIZE); - for (Map.Entry<String, ? extends FieldModel> entry2 : entries2) { - FieldModel model2 = entry2.getValue(); - offset += (model2.nativeSize() + 7) >> 3; - } + int size = computeNonScalarOffset(dvmodel, type); + methodNonScalarGet(getterSetters, getter, name, type, model); + methodNonScalarWriteMarshall(writeMarshal, name, model); + methodNonScalarReadMarshall(readMarshal, name, model); + methodNonScalarBytes(nestedBytes, name, NAME, size, model); + + offset += computeOffset(size, model); } } fieldDeclarations.append("\n") .append(" private Bytes _bytes;\n") .append(" private long _offset;\n"); StringBuilder sb = new StringBuilder(); - sb.append("package ").append(dvmodel.type().getPackage().getName()).append(";\n\n"); + appendPackage(dvmodel, sb); sb.append("import static ").append(Compare.class.getName()).append(".*;\n"); - for (Class aClass : imported) { - sb.append("import ").append(aClass.getName()).append(";\n"); - } - sb.append("\npublic class ").append(dvmodel.type().getSimpleName()) - .append("£native implements ").append(dvmodel.type().getSimpleName()) - .append(", BytesMarshallable, Byteable, Copyable<").append(dvmodel.type().getName()).append("> {\n"); + appendImported(imported, sb); + sb.append("\npublic class ").append(simpleName(dvmodel.type())) + .append("$$Native implements ").append(simpleName(dvmodel.type()).replace('$', '.')) + .append(", BytesMarshallable, Byteable, Copyable<").append(normalize(dvmodel.type())).append("> {\n"); sb.append(staticFieldDeclarations).append('\n'); sb.append(fieldDeclarations).append('\n'); sb.append(getterSetters); - sb.append(" public void copyFrom(").append(dvmodel.type().getName()).append(" from) {\n"); - sb.append(copy); - sb.append(" }\n\n"); - sb.append(" public void writeMarshallable(Bytes out) {\n"); - sb.append(writeMarshal); - sb.append(" }\n"); - sb.append(" public void readMarshallable(Bytes in) {\n"); - sb.append(readMarshal); - sb.append(" }\n"); - sb.append(" public void bytes(Bytes bytes) {\n"); - sb.append(" bytes(bytes, 0L);\n"); - sb.append(" }\n"); - sb.append(" public void bytes(Bytes bytes, long offset) {\n"); - sb.append(" this._bytes = bytes;\n"); - sb.append(" this._offset = offset;\n"); - sb.append(nestedBytes); - sb.append(" }\n"); - sb.append(" public Bytes bytes() {\n"); - sb.append(" return _bytes;\n"); - sb.append(" }\n"); - sb.append(" public int maxSize() {\n"); - sb.append(" return ").append(offset).append(";\n"); - sb.append(" }\n"); - generateObjectMethods(sb, dvmodel, entries); + sb.append(" @Override\n") + .append(" public void copyFrom(").append(normalize(dvmodel.type())).append(" from) {\n") + .append(copy) + .append(" }\n\n"); + sb.append(" @Override\n") + .append(" public void writeMarshallable(Bytes out) {\n") + .append(writeMarshal) + .append(" }\n"); + sb.append(" @Override\n") + .append(" public void readMarshallable(Bytes in) {\n") + .append(readMarshal) + .append(" }\n"); + sb.append(" @Override\n") + .append(" public void bytes(Bytes bytes, long offset) {\n") + .append(" this._bytes = bytes;\n") + .append(" this._offset = offset;\n") + .append(nestedBytes) + .append(" }\n"); + sb.append(" @Override\n") + .append(" public Bytes bytes() {\n") + .append(" return _bytes;\n") + .append(" }\n"); + sb.append(" @Override\n") + .append(" public long offset() {\n") + .append(" return _offset;\n") + .append(" }\n"); + sb.append(" @Override\n") + .append(" public int maxSize() {\n") + .append(" return ").append(offset).append(";\n") + .append(" }\n"); + + generateObjectMethods(sb, dvmodel, entries, true); sb.append("}\n"); // System.out.println(sb); return sb.toString(); @@ -480,4 +1027,167 @@ public class DataValueGenerator { public void setDumpCode(boolean dumpCode) { this.dumpCode = dumpCode; } + + private void methodSet(StringBuilder getterSetters, Method setter, Class type, String NAME, FieldModel model, boolean isVolatile) { + Class<?> setterType = setter.getParameterTypes()[setter.getParameterTypes().length - 1]; + String write = "write"; + if (isVolatile) write = "writeOrdered"; + if (model.type() == Date.class) { + getterSetters.append("\n\n public void ").append(setter.getName()).append('(').append(normalize(setterType)).append(" $) {\n"); + getterSetters.append(" _bytes.").append(write).append("Long").append("(").append(NAME).append(", "); + } else if(Enum.class.isAssignableFrom(type)){ + getterSetters.append("\n\n public void ").append(setter.getName()).append('(').append(normalize(setterType)).append(" $) {\n"); + getterSetters.append(" _bytes.").append(write).append("Enum").append("("); + } else if (!model.isArray()) { + getterSetters.append("\n\n public void ").append(setter.getName()).append('(').append(normalize(setterType)).append(" $) {\n"); + getterSetters.append(" _bytes.").append(write).append(bytesType(type)).append("(").append(NAME).append(", "); + + } else { + getterSetters.append(" public void ").append(setter.getName()).append("(int i, "); + getterSetters.append(normalize(setterType)).append(" $) {\n"); + getterSetters.append(boundsCheck(model.indexSize().value())); + getterSetters.append(" _bytes.").append(write).append(bytesType(type)).append("(").append(NAME); + getterSetters.append(" + i * ").append((model.nativeSize() + 7) >> 3).append(", "); + } + + if (CharSequence.class.isAssignableFrom(type)) + getterSetters.append(model.size().value()).append(", "); + + if (model.type() == Date.class) { + getterSetters.append("$.getTime());\n"); + } else { + getterSetters.append("$);\n"); + } + getterSetters.append(" }\n\n"); + } + private void methodGet(StringBuilder getterSetters, Class type, String NAME, boolean isVolatile, String name) { + String read = "read"; + if (isVolatile) read = "readVolatile"; + getterSetters.append(" public ").append(normalize(type)).append(' ').append(name).append("() {\n"); + getterSetters.append(" return _bytes.").append(read).append(bytesType(type)).append("(").append(NAME).append(");\n"); + getterSetters.append(" }\n\n"); + } + + private void methodGet(StringBuilder getterSetters, Method getter, Class type, String NAME, FieldModel model, boolean isVolatile) { + String read = "read"; + if (isVolatile) read = "readVolatile"; + + if(model.type() == Date.class){ + getterSetters.append(" public ").append(normalize(type)).append(' ').append(getter.getName()).append("() {\n"); + getterSetters.append(" return new Date( _bytes.").append(read).append("Long").append("(").append(NAME).append("));\n"); + } else if (Enum.class.isAssignableFrom(model.type())) { + getterSetters.append(" public ").append(normalize(type)).append(' ').append(getter.getName()).append("() {\n"); + //getterSetters.append(" return _bytes.").append(read).append("Enum").append("(").append(NAME).append(");\n"); + getterSetters.append(" return _bytes.").append(read).append("Enum").append("(").append(type.getName()).append(".class);\n"); + } else if (!model.isArray()) { + getterSetters.append(" public ").append(normalize(type)).append(' ').append(getter.getName()).append("() {\n"); + getterSetters.append(" return _bytes.").append(read).append(bytesType(type)).append("(").append(NAME).append(");\n"); + + } else { + getterSetters.append(" public ").append(normalize(type)).append(' ').append(getter.getName()).append("(int i) {\n"); + getterSetters.append(boundsCheck(model.indexSize().value())); + getterSetters.append(" return _bytes.").append(read).append(bytesType(type)).append("(").append(NAME); + getterSetters.append(" + i * ").append((model.nativeSize() + 7) >> 3); + getterSetters.append(");\n"); + } + getterSetters.append(" }\n\n"); + } + + private void methodNonScalarWriteMarshall(StringBuilder writeMarshal, String name, FieldModel model) { + if (!model.isArray()) { + writeMarshal.append(" _").append(name).append(".writeMarshallable(out);\n"); + + } else { + writeMarshal.append(" for (int i = 0; i < ").append(model.indexSize().value()).append("; i++){\n"); + writeMarshal.append(" _").append(name).append("[i].writeMarshallable(out);\n"); + writeMarshal.append(" }\n"); + } + } + + private void methodReadMarshall(StringBuilder readMarshal, Method getter, Method setter, Class type, FieldModel model) { + if(model.type() == Date.class){ + if (getter != null && setter != null) + readMarshal.append(" ").append(setter.getName()).append("((Date)in.read").append(bytesType(type)).append("());\n"); + } else if (Enum.class.isAssignableFrom(model.type())) { + if (getter != null && setter != null) + readMarshal.append(" ").append(setter.getName()).append("(in.readEnum(" + model.type().getName() + ".class));\n"); + } else if (!model.isArray()) { + if (getter != null && setter != null) + readMarshal.append(" ").append(setter.getName()).append("(in.read").append(bytesType(type)).append("());\n"); + + } else { + readMarshal.append(" for (int i = 0; i < ").append(model.indexSize().value()).append("; i++){\n"); + readMarshal.append(" ").append(setter.getName()).append("(i, in.read").append(bytesType(type)).append("());\n"); + readMarshal.append(" }\n"); + } + } + + private void methodNonScalarReadMarshall(StringBuilder readMarshal, String name, FieldModel model) { + if (!model.isArray()) { + readMarshal.append(" _").append(name).append(".readMarshallable(in);\n"); + + } else { + readMarshal.append(" for (int i = 0; i < ").append(model.indexSize().value()).append("; i++){\n"); + readMarshal.append(" _").append(name).append("[i].readMarshallable(in);\n"); + readMarshal.append(" }\n"); + } + } + + private void nonScalarFieldDeclaration(StringBuilder fieldDeclarations, Class type, String name, FieldModel model) { + fieldDeclarations.append(" private final ").append(type.getName()).append("$$Native _").append(name); + if (!model.isArray()) { + fieldDeclarations.append(" = new ").append(type.getName()).append("$$Native();\n"); + + } else { + fieldDeclarations.append("[] = new ").append(type.getName()).append("$$Native[").append(model.indexSize().value()).append("];\n"); + fieldDeclarations.append(" {\n") + .append(" for (int i = 0; i < ").append(model.indexSize().value()).append("; i++)\n") + .append(" _").append(name).append("[i] = new ").append(type.getName()).append("$$Native();\n") + .append(" }\n"); + } + } + + private void methodNonScalarSet(StringBuilder getterSetters, Method setter, String name, Class type, FieldModel model) { + Class<?> setterType = setter.getParameterTypes()[setter.getParameterTypes().length - 1]; + + if (!model.isArray()) { + getterSetters.append(" public void ").append(setter.getName()).append('(').append(normalize(setterType)).append(" $) {\n"); + if (type == String.class && setterType != String.class) + getterSetters.append(" _").append(name).append(" = " + nullAwareToString("$") + ";\n"); + else + getterSetters.append(" _").append(name).append(".copyFrom($);\n"); + + } else { + getterSetters.append(" public void ").append(setter.getName()).append("(int i, ").append(normalize(setterType)).append(" $) {\n"); + if (type == String.class && setterType != String.class) + getterSetters.append(" _").append(name).append("[i] = " + nullAwareToString("$") + ";\n"); + else + getterSetters.append(" _").append(name).append("[i].copyFrom($);\n"); + } + getterSetters.append(" }\n\n"); + } + + private void methodNonScalarGet(StringBuilder getterSetters, Method getter, String name, Class type, FieldModel model) { + if (!model.isArray()) { + getterSetters.append(" public ").append(normalize(type)).append(' ').append(getter.getName()).append("() {\n"); + getterSetters.append(" return _").append(name).append(";\n"); + + } else { + getterSetters.append(" public ").append(normalize(type)).append(' ').append(getter.getName()).append("(int i) {\n"); + getterSetters.append(" return _").append(name).append("[i];\n"); + } + getterSetters.append(" }\n\n"); + } + + private void methodNonScalarBytes(StringBuilder nestedBytes, String name, String NAME, int size, FieldModel model) { + if (!model.isArray()) { + nestedBytes.append(" ((Byteable) _").append(name).append(").bytes(bytes, ").append(NAME).append(");\n"); + + } else { + nestedBytes.append(" for (int i = 0; i < ").append(model.indexSize().value()).append("; i++){\n"); + nestedBytes.append(" ((Byteable) _").append(name).append("[i]).bytes(bytes, ").append(NAME); + nestedBytes.append(" + (i * ").append(size).append("));\n"); + nestedBytes.append(" }\n"); + } + } } diff --git a/lang/src/main/java/net/openhft/lang/model/DataValueModel.java b/lang/src/main/java/net/openhft/lang/model/DataValueModel.java index 23e1567..7528a73 100644 --- a/lang/src/main/java/net/openhft/lang/model/DataValueModel.java +++ b/lang/src/main/java/net/openhft/lang/model/DataValueModel.java @@ -1,17 +1,17 @@ /* - * Copyright 2013 Peter Lawrey + * Copyright (C) 2015 higherfrequencytrading.com * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as published by + * the Free Software Foundation, either version 3 of the License. * - * http://www.apache.org/licenses/LICENSE-2.0 + * 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 Lesser General Public License for more details. * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. + * You should have received a copy of the GNU Lesser General Public License + * along with this program. If not, see <http://www.gnu.org/licenses/>. */ package net.openhft.lang.model; diff --git a/lang/src/main/java/net/openhft/lang/model/DataValueModelImpl.java b/lang/src/main/java/net/openhft/lang/model/DataValueModelImpl.java index bc5b03b..ad20852 100644..100755 --- a/lang/src/main/java/net/openhft/lang/model/DataValueModelImpl.java +++ b/lang/src/main/java/net/openhft/lang/model/DataValueModelImpl.java @@ -1,41 +1,43 @@ /* - * Copyright 2013 Peter Lawrey + * Copyright (C) 2015 higherfrequencytrading.com * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as published by + * the Free Software Foundation, either version 3 of the License. * - * http://www.apache.org/licenses/LICENSE-2.0 + * 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 Lesser General Public License for more details. * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. + * You should have received a copy of the GNU Lesser General Public License + * along with this program. If not, see <http://www.gnu.org/licenses/>. */ package net.openhft.lang.model; import net.openhft.lang.io.serialization.BytesMarshallable; import net.openhft.lang.model.constraints.Digits; +import net.openhft.lang.model.constraints.Group; import net.openhft.lang.model.constraints.MaxSize; import net.openhft.lang.model.constraints.Range; import java.io.Externalizable; import java.lang.annotation.Annotation; import java.lang.reflect.Method; -import java.util.HashMap; -import java.util.Map; -import java.util.Set; -import java.util.TreeMap; +import java.lang.reflect.Modifier; +import java.util.*; + +import static net.openhft.lang.MemoryUnit.BITS; +import static net.openhft.lang.MemoryUnit.BYTES; /** - * User: peter.lawrey - * Date: 06/10/13 - * Time: 17:23 + * User: peter.lawrey Date: 06/10/13private static final int VALUE Time: 17:23 */ public class DataValueModelImpl<T> implements DataValueModel<T> { - static final Map<Class, Integer> HEAP_SIZE_MAP = new HashMap<Class, Integer>(); + private static final Map<Class, Integer> HEAP_SIZE_MAP = new HashMap<Class, Integer>(); + private static final String VOLATILE_GETTER_PREFIX = "volatile"; + private static final java.lang.String ORDERED_SETTER_PREFIX = "ordered"; static { HEAP_SIZE_MAP.put(boolean.class, 1); @@ -46,6 +48,7 @@ public class DataValueModelImpl<T> implements DataValueModel<T> { HEAP_SIZE_MAP.put(float.class, 32); HEAP_SIZE_MAP.put(long.class, 64); HEAP_SIZE_MAP.put(double.class, 64); + HEAP_SIZE_MAP.put(Date.class, 64); } private final Map<String, FieldModelImpl> fieldModelMap = new TreeMap<String, FieldModelImpl>(); @@ -54,6 +57,7 @@ public class DataValueModelImpl<T> implements DataValueModel<T> { public DataValueModelImpl(Class<T> type) { this.type = type; + if (!type.isInterface()) throw new IllegalArgumentException("type must be an interface, was " + type); @@ -66,6 +70,11 @@ public class DataValueModelImpl<T> implements DataValueModel<T> { || declaringClass == Copyable.class || declaringClass == Byteable.class) continue; + + // ignore the default or static methods + if(isMethodDefaultOrStatic(method)) + continue; + String name = method.getName(); Class<?>[] parameterTypes = method.getParameterTypes(); final Class<?> returnType = method.getReturnType(); @@ -89,14 +98,38 @@ public class DataValueModelImpl<T> implements DataValueModel<T> { fm.tryLock(method); break; } + String name6 = getSizeOf(name); + if (name6 != null && returnType == int.class) { + FieldModelImpl fm = acquireField(name6); + fm.sizeOf(method); + break; + } if (returnType == void.class) throw new IllegalArgumentException("void () not supported " + method); + String name2 = getGetter(name, returnType); - FieldModelImpl fm = acquireField(name2); - fm.getter(method); + if (isVolatileGetter(name2)) { + FieldModelImpl fm = acquireField(volatileGetterFieldName(name2)); + fm.volatileGetter(method); + fm.setVolatile(true); + + } else { + FieldModelImpl fm = acquireField(name2); + fm.getter(method); + } + break; } + case 1: { + + String name7 = getUsing(name, method); + if (name7 != null) { + FieldModelImpl fm = acquireField(name7); + fm.getUsing(method); + break; + } + String name5 = getTryLockNanos(name); if (name5 != null && returnType == boolean.class) { FieldModelImpl fm = acquireField(name5); @@ -118,20 +151,35 @@ public class DataValueModelImpl<T> implements DataValueModel<T> { break; } - String name6 = getGetter(name, returnType); + String name6 = getGetterAt(name, returnType); if (name6 != null && parameterTypes[0] == int.class && returnType != void.class) { - FieldModelImpl fm = acquireField(name6); - fm.indexedGetter(method); + if (isVolatileGetter(name6)) { + FieldModelImpl fm = acquireField(volatileGetterFieldName(name6)); + fm.volatileIndexedGetter(method); + fm.setVolatile(true); + + } else { + FieldModelImpl fm = acquireField(name6); + fm.indexedGetter(method); + } break; } if (returnType != void.class) throw new IllegalArgumentException("setter must be void " + method); + String name2 = getSetter(name); - FieldModelImpl fm = acquireField(name2); - fm.setter(method); + if (isOrderedSetter(name2)) { + FieldModelImpl fm = acquireField(orderedSetterFieldName(name2)); + fm.orderedSetter(method); + + } else { + FieldModelImpl fm = acquireField(name2); + fm.setter(method); + } break; } + case 2: { String name2 = getCAS(name); if (name2 != null && returnType == boolean.class) { @@ -139,29 +187,53 @@ public class DataValueModelImpl<T> implements DataValueModel<T> { fm.cas(method); break; } - String name3 = getSetter(name); + String name3 = getSetterAt(name); if (name3 != null && parameterTypes[0] == int.class && returnType == void.class) { - FieldModelImpl fm = acquireField(name3); - fm.indexedSetter(method); + if (isOrderedSetter(name3)) { + FieldModelImpl fm = acquireField(orderedSetterFieldName(name3)); + fm.orderedIndexedSetter(method); + + } else { + FieldModelImpl fm = acquireField(name3); + fm.indexedSetter(method); + } break; } } + default: { throw new IllegalArgumentException("method not supported " + method); } } } + for (Map.Entry<String, FieldModelImpl> entry : fieldModelMap.entrySet()) { FieldModelImpl model = entry.getValue(); - if (model.getter() == null || (model.setter() == null && model.getter().getReturnType().isPrimitive())) - if (model.indexedGetter() == null || (model.indexedSetter() == null && model.indexedGetter().getReturnType().isPrimitive())) - throw new IllegalArgumentException("Field " + entry.getKey() + " must have a getter and setter."); + if ((model.getter() == null && model.getUsing() == null) || (model.setter() == null && model + .getter() + .getReturnType() + .isPrimitive())) + if (model.volatileGetter() == null || (model.orderedSetter() == null && model.volatileGetter().getReturnType().isPrimitive())) + if (model.indexedGetter() == null || (model.indexedSetter() == null && model.indexedGetter().getReturnType().isPrimitive())) + if (model.volatileIndexedGetter() == null || (model.orderedIndexedSetter() == null && model.volatileIndexedGetter().getReturnType().isPrimitive())) + if (model.busyLock() == null || model.unlock() == null) + throw new IllegalArgumentException("Field " + entry.getKey() + " must have a getter & setter, or getAt & setAt, or busyLock & unlock."); + if (model.indexedGetter() != null || model.indexedSetter() != null) + if (model.indexSize() == null) + throw new IllegalStateException("You must set a MaxSize for the range of the index for the getter or setter"); + Class ftype = model.type(); if (!isScalar(ftype) && !nestedMap.containsKey(ftype)) nestedMap.put(ftype, new DataValueModelImpl(ftype)); } } + public static int heapSize(Class primitiveType) { + if (!primitiveType.isPrimitive()) + throw new IllegalArgumentException(); + return (int) BYTES.alignAndConvert(HEAP_SIZE_MAP.get(primitiveType), BITS); + } + private static String getCAS(String name) { final int len = 14; if (name.length() > len && name.startsWith("compareAndSwap") && Character.isUpperCase(name.charAt(len))) @@ -169,6 +241,13 @@ public class DataValueModelImpl<T> implements DataValueModel<T> { return null; } + private static String getSizeOf(String name) { + final int len = 6; + if (name.length() > len && name.startsWith("sizeOf") && Character.isUpperCase(name.charAt(len))) + return Character.toLowerCase(name.charAt(len)) + name.substring(len + 1); + return null; + } + private static String getAtomicAdder(String name) { final int len = 9; if (name.length() > len && name.startsWith("addAtomic") && Character.isUpperCase(name.charAt(len))) @@ -190,6 +269,15 @@ public class DataValueModelImpl<T> implements DataValueModel<T> { return name; } + private static String getSetterAt(String name) { + final int len = 3; + final int len2 = 2; + if (name.length() > len + len2 && name.startsWith("set") && Character.isUpperCase( + name.charAt(len)) && name.endsWith("At")) + return Character.toLowerCase(name.charAt(len)) + name.substring(len + 1, name.length() - len2); + return name; + } + private static String getGetter(String name, Class returnType) { if (name.length() > 3 && name.startsWith("get") && Character.isUpperCase(name.charAt(3))) return Character.toLowerCase(name.charAt(3)) + name.substring(4); @@ -199,34 +287,82 @@ public class DataValueModelImpl<T> implements DataValueModel<T> { return name; } - private String getBusyLock(String name) { + private static String getUsing(String name, Method method) { + Class<?> returnType = method.getReturnType(); + if (method.getParameterTypes().length != 1) + return null; + + Class<?> parameter = method.getParameterTypes()[0]; + + if ((returnType == StringBuilder.class || returnType == void.class) && parameter == + StringBuilder.class && + name.length() > "getUsing".length() && name.startsWith + ("getUsing") && Character.isUpperCase(name.charAt("getUsing".length()))) + return Character.toLowerCase(name.charAt("getUsing".length())) + name.substring("getUsing" + .length() + 1); + return null; + } + + private static String getGetterAt(String name, Class returnType) { + final int len = 3; + final int len2 = 2; + if (name.length() > len + len2 && name.startsWith("get") && Character.isUpperCase( + name.charAt(len)) && name.endsWith("At")) + return Character.toLowerCase(name.charAt(len)) + name.substring(4, name.length() - len2); + return name; + } + + private static String getBusyLock(String name) { final int len = 8; if (name.length() > len && name.startsWith("busyLock") && Character.isUpperCase(name.charAt(len))) return Character.toLowerCase(name.charAt(len)) + name.substring(len + 1); return null; } - private String getUnlock(String name) { + private static String getUnlock(String name) { final int len = 6; if (name.length() > len && name.startsWith("unlock") && Character.isUpperCase(name.charAt(len))) return Character.toLowerCase(name.charAt(len)) + name.substring(len + 1); return null; } - private String getTryLockNanos(String name) { + private static String getTryLockNanos(String name) { final int len = 12; if (name.length() > len && name.startsWith("tryLockNanos") && Character.isUpperCase(name.charAt(len))) return Character.toLowerCase(name.charAt(len)) + name.substring(len + 1); return null; } - private String getTryLock(String name) { + private static String getTryLock(String name) { final int len = 7; if (name.length() > len && name.startsWith("tryLock") && Character.isUpperCase(name.charAt(len))) return Character.toLowerCase(name.charAt(len)) + name.substring(len + 1); return null; } + public boolean isMethodDefaultOrStatic(Method method) { + return ((method.getModifiers() & (Modifier.ABSTRACT | Modifier.PUBLIC)) == + Modifier.PUBLIC) && method.getDeclaringClass().isInterface(); + } + + private boolean isOrderedSetter(String name2) { + return name2.startsWith(ORDERED_SETTER_PREFIX) ? true : false; + } + + private boolean isVolatileGetter(String name2) { + return name2.startsWith(VOLATILE_GETTER_PREFIX) ? true : false; + } + + private String volatileGetterFieldName(String name) { + name = name.substring(VOLATILE_GETTER_PREFIX.length()); + return Character.toLowerCase(name.charAt(0)) + name.substring(1); + } + + private String orderedSetterFieldName(String name) { + name = name.substring(ORDERED_SETTER_PREFIX.length()); + return Character.toLowerCase(name.charAt(0)) + name.substring(1); + } + private FieldModelImpl acquireField(String name) { FieldModelImpl fieldModelImpl = fieldModelMap.get(name); if (fieldModelImpl == null) @@ -241,7 +377,7 @@ public class DataValueModelImpl<T> implements DataValueModel<T> { } public boolean isScalar(Class type) { - return type.isPrimitive() || CharSequence.class.isAssignableFrom(type); + return type.isPrimitive() || CharSequence.class.isAssignableFrom(type) || Enum.class.isAssignableFrom(type) || Date.class == type; } @Override @@ -265,19 +401,28 @@ public class DataValueModelImpl<T> implements DataValueModel<T> { private final String name; private Method getter, setter; + private Method volatileGetter; + private Method orderedSetter; private Digits digits; private Range range; private MaxSize maxSize; + private Group group; private MaxSize indexSize; private Method adder; private Method atomicAdder; + private Method getUsing; private Method cas; private Method tryLockNanos; private Method tryLock; private Method busyLock; private Method unlock; - private Method indexedGetter; - private Method indexedSetter; + private Method getterAt; + private Method setterAt; + private Method volatileGetterAt; + private Method orderedSetterAt; + private Method sizeOf; + private boolean isArray = false; + private boolean isVolatile = false; public FieldModelImpl(String name) { this.name = name; @@ -287,6 +432,20 @@ public class DataValueModelImpl<T> implements DataValueModel<T> { return name; } + public boolean isArray() { + return isArray; + } + + @Override + public boolean isVolatile() { + return isVolatile; + } + + @Override + public void setVolatile(boolean isVolatile) { + this.isVolatile = isVolatile; + } + public void getter(Method getter) { this.getter = getter; } @@ -305,16 +464,54 @@ public class DataValueModelImpl<T> implements DataValueModel<T> { if (a instanceof MaxSize) maxSize = (MaxSize) a; } - } + for (Annotation a : setter.getAnnotations()) { + if (a instanceof Group) + group = (Group) a; + } + } public Method setter() { return setter; } + public void volatileGetter(Method volatileGetter) { + this.volatileGetter = volatileGetter; + } + + public Method volatileGetter() { + return volatileGetter; + } + + public void orderedSetter(Method orderedSetter) { + this.orderedSetter = orderedSetter; + for (Annotation a : orderedSetter.getParameterAnnotations()[0]) { + if (a instanceof Digits) + digits = (Digits) a; + if (a instanceof Range) + range = (Range) a; + if (a instanceof MaxSize) + maxSize = (MaxSize) a; + } + + for (Annotation a : orderedSetter.getAnnotations()) { + if (a instanceof Group) + group = (Group) a; + } + } + + public Method orderedSetter() { + return orderedSetter; + } + @Override public Class<T> type() { return (Class<T>) (getter != null ? getter.getReturnType() : - indexedGetter != null ? indexedGetter.getReturnType() : null); + volatileGetter != null ? volatileGetter.getReturnType() : + getterAt != null ? getterAt.getReturnType() : + volatileGetterAt != null ? volatileGetterAt.getReturnType() : + unlock != null ? int.class : + setter != null && setter.getParameterTypes().length == 1 ? + setter.getParameterTypes()[0] : null); } public void adder(Method method) { @@ -338,10 +535,7 @@ public class DataValueModelImpl<T> implements DataValueModel<T> { Integer size = HEAP_SIZE_MAP.get(type()); if (size != null) return size; - MaxSize maxSize2 = size(); - if (maxSize2 == null) - throw new AssertionError(type() + " without a @MaxSize not supported for native types"); - return maxSize2.value() << 3; + return size().value() << 3; } @Override @@ -356,19 +550,27 @@ public class DataValueModelImpl<T> implements DataValueModel<T> { @Override public MaxSize size() { + if (maxSize == null) + throw new IllegalStateException("Field " + name + " is missing @MaxSize on the setter"); return maxSize; } @Override + public Group group() { + return group; + } + + @Override public String toString() { return "FieldModel{" + "name='" + name + '\'' + - ", getter=" + (indexedGetter != null ? indexedGetter : getter) + - ", setter=" + (indexedSetter != null ? indexedSetter : setter) + + ", getter=" + (getterAt != null ? getterAt : getter) + + ", setter=" + (setterAt != null ? setterAt : setter) + + (unlock == null ? "" : ", busyLock=" + busyLock + ", tryLock=" + tryLock + ", unlock=" + unlock) + (digits == null ? "" : ", digits= " + digits) + (range == null ? "" : ", range= " + range) + (maxSize == null ? "" : ", size= " + maxSize) + - ((indexedGetter == null && indexedSetter == null) ? "" : ", indexSize= " + indexSize) + + ((getterAt == null && setterAt == null) ? "" : ", indexSize= " + indexSize.toString().replace("@net.openhft.lang.model.constraints.", "")) + '}'; } @@ -376,6 +578,10 @@ public class DataValueModelImpl<T> implements DataValueModel<T> { atomicAdder = method; } + public void getUsing(Method method) { + getUsing = method; + } + public Method atomicAdder() { return atomicAdder; } @@ -388,6 +594,14 @@ public class DataValueModelImpl<T> implements DataValueModel<T> { return cas; } + public void sizeOf(Method method) { + sizeOf = method; + } + + public Method sizeOf() { + return sizeOf; + } + public void tryLockNanos(Method method) { tryLockNanos = method; } @@ -430,21 +644,28 @@ public class DataValueModelImpl<T> implements DataValueModel<T> { } public void indexedGetter(Method indexedGetter) { - this.indexedGetter = indexedGetter; + isArray = true; + this.getterAt = indexedGetter; indexAnnotations(indexedGetter); } public Method indexedGetter() { - return indexedGetter; + return getterAt; } public void indexedSetter(Method indexedSetter) { - this.indexedSetter = indexedSetter; + isArray = true; + this.setterAt = indexedSetter; indexAnnotations(indexedSetter); } + public Method indexedSetter() { + return setterAt; + } + public void indexAnnotations(Method method) { - for (Annotation a : method.getParameterAnnotations()[0]) { + Annotation[][] parameterAnnotations = method.getParameterAnnotations(); + for (Annotation a : parameterAnnotations[0]) { // if (a instanceof Digits) // digits = (Digits) a; // if (a instanceof Range) @@ -452,10 +673,41 @@ public class DataValueModelImpl<T> implements DataValueModel<T> { if (a instanceof MaxSize) indexSize = (MaxSize) a; } + if( parameterAnnotations.length > 1 ) { + for (Annotation a : parameterAnnotations[1]) { + if (a instanceof Digits) + digits = (Digits) a; + if (a instanceof Range) + range = (Range) a; + if (a instanceof MaxSize) + maxSize = (MaxSize) a; + } + } } - public Method indexedSetter() { - return indexedSetter; + public void volatileIndexedGetter(Method volatileIndexedGetter) { + isArray = true; + this.volatileGetterAt = volatileIndexedGetter; + indexAnnotations(volatileIndexedGetter); + } + + public Method volatileIndexedGetter() { + return volatileGetterAt; + } + + public void orderedIndexedSetter(Method orderedIndexedSetter) { + isArray = true; + this.orderedSetterAt = orderedIndexedSetter; + indexAnnotations(orderedIndexedSetter); + } + + public Method orderedIndexedSetter() { + return orderedSetterAt; + } + + @Override + public Method getUsing() { + return getUsing; } } } diff --git a/lang/src/main/java/net/openhft/lang/model/DataValueModels.java b/lang/src/main/java/net/openhft/lang/model/DataValueModels.java index bda9620..36c229b 100644..100755 --- a/lang/src/main/java/net/openhft/lang/model/DataValueModels.java +++ b/lang/src/main/java/net/openhft/lang/model/DataValueModels.java @@ -1,17 +1,17 @@ /* - * Copyright 2013 Peter Lawrey + * Copyright (C) 2015 higherfrequencytrading.com * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as published by + * the Free Software Foundation, either version 3 of the License. * - * http://www.apache.org/licenses/LICENSE-2.0 + * 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 Lesser General Public License for more details. * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. + * You should have received a copy of the GNU Lesser General Public License + * along with this program. If not, see <http://www.gnu.org/licenses/>. */ package net.openhft.lang.model; @@ -28,20 +28,22 @@ public enum DataValueModels { ; private static final Map<Class, DataValueModel> MODEL_MAP = new WeakHashMap<Class, DataValueModel>(); - public static synchronized <T> DataValueModel<T> getModel(Class<T> tClass) { + private static synchronized <T> DataValueModel<T> getModel(Class<T> tClass) { return MODEL_MAP.get(tClass); } - public static synchronized <T> void putModel(Class<T> tClass, DataValueModel<T> model) { + private static synchronized <T> void putModel(Class<T> tClass, DataValueModel<T> model) { MODEL_MAP.put(tClass, model); } public static <T> DataValueModel<T> acquireModel(Class<T> tClass) { + if (!tClass.isInterface() || tClass.getClassLoader() == null) + throw new IllegalArgumentException(tClass + " not supported"); DataValueModel<T> model = getModel(tClass); - if (model == null) { - model = new DataValueModelImpl<T>(tClass); - putModel(tClass, model); - } + if (model == null) { + model = new DataValueModelImpl<T>(tClass); + putModel(tClass, model); + } return model; } } diff --git a/lang/src/main/java/net/openhft/lang/model/FieldModel.java b/lang/src/main/java/net/openhft/lang/model/FieldModel.java index 33869d0..d5caec4 100644 --- a/lang/src/main/java/net/openhft/lang/model/FieldModel.java +++ b/lang/src/main/java/net/openhft/lang/model/FieldModel.java @@ -1,22 +1,23 @@ /* - * Copyright 2013 Peter Lawrey + * Copyright (C) 2015 higherfrequencytrading.com * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as published by + * the Free Software Foundation, either version 3 of the License. * - * http://www.apache.org/licenses/LICENSE-2.0 + * 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 Lesser General Public License for more details. * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. + * You should have received a copy of the GNU Lesser General Public License + * along with this program. If not, see <http://www.gnu.org/licenses/>. */ package net.openhft.lang.model; import net.openhft.lang.model.constraints.Digits; +import net.openhft.lang.model.constraints.Group; import net.openhft.lang.model.constraints.MaxSize; import net.openhft.lang.model.constraints.Range; @@ -34,6 +35,20 @@ public interface FieldModel<T> { Method setter(); + Method indexedGetter(); + + Method indexedSetter(); + + Method volatileGetter(); + + Method orderedSetter(); + + Method volatileIndexedGetter(); + + Method orderedIndexedSetter(); + + Method getUsing(); + Method adder(); Method atomicAdder(); @@ -48,6 +63,8 @@ public interface FieldModel<T> { Method unlock(); + Method sizeOf(); + Class<T> type(); int heapSize(); @@ -59,4 +76,14 @@ public interface FieldModel<T> { Range range(); MaxSize size(); + + MaxSize indexSize(); + + boolean isArray(); + + boolean isVolatile(); + + void setVolatile(boolean isVolatile); + + Group group(); } diff --git a/lang/src/main/java/net/openhft/lang/model/HeapCodeGenerator.java b/lang/src/main/java/net/openhft/lang/model/HeapCodeGenerator.java index 78886a4..b7cea1b 100644 --- a/lang/src/main/java/net/openhft/lang/model/HeapCodeGenerator.java +++ b/lang/src/main/java/net/openhft/lang/model/HeapCodeGenerator.java @@ -1,22 +1,22 @@ /* - * Copyright 2013 Peter Lawrey + * Copyright (C) 2015 higherfrequencytrading.com * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as published by + * the Free Software Foundation, either version 3 of the License. * - * http://www.apache.org/licenses/LICENSE-2.0 + * 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 Lesser General Public License for more details. * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. + * You should have received a copy of the GNU Lesser General Public License + * along with this program. If not, see <http://www.gnu.org/licenses/>. */ package net.openhft.lang.model; -public enum HeapCodeGenerator implements CodeGenerator { +enum HeapCodeGenerator implements CodeGenerator { /* GET_VALUE { public void addCode(CodeModel codeModel, Method method) { StringBuilder sb = codeModel.acquireMethod() diff --git a/lang/src/main/java/net/openhft/lang/model/Out.java b/lang/src/main/java/net/openhft/lang/model/Out.java new file mode 100644 index 0000000..33db327 --- /dev/null +++ b/lang/src/main/java/net/openhft/lang/model/Out.java @@ -0,0 +1,34 @@ +/* + * Copyright (C) 2015 higherfrequencytrading.com + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as published by + * the Free Software Foundation, either version 3 of the License. + * + * 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 Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with this program. If not, see <http://www.gnu.org/licenses/>. + */ + +package net.openhft.lang.model; + +/** + * The annotation indicates the argument can be mutated. In general, arguments should not be mutated. + */ + +import java.lang.annotation.Documented; +import java.lang.annotation.Retention; +import java.lang.annotation.Target; + +import static java.lang.annotation.ElementType.PARAMETER; +import static java.lang.annotation.RetentionPolicy.CLASS; + +@Documented +@Retention(CLASS) +@Target({PARAMETER}) +public @interface Out { +} diff --git a/lang/src/main/java/net/openhft/lang/model/constraints/Digits.java b/lang/src/main/java/net/openhft/lang/model/constraints/Digits.java index 4a1b1ee..4f76904 100644 --- a/lang/src/main/java/net/openhft/lang/model/constraints/Digits.java +++ b/lang/src/main/java/net/openhft/lang/model/constraints/Digits.java @@ -1,17 +1,17 @@ /* - * Copyright 2013 Peter Lawrey + * Copyright (C) 2015 higherfrequencytrading.com * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as published by + * the Free Software Foundation, either version 3 of the License. * - * http://www.apache.org/licenses/LICENSE-2.0 + * 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 Lesser General Public License for more details. * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. + * You should have received a copy of the GNU Lesser General Public License + * along with this program. If not, see <http://www.gnu.org/licenses/>. */ package net.openhft.lang.model.constraints; diff --git a/lang/src/main/java/net/openhft/lang/model/constraints/Group.java b/lang/src/main/java/net/openhft/lang/model/constraints/Group.java new file mode 100644 index 0000000..caff68b --- /dev/null +++ b/lang/src/main/java/net/openhft/lang/model/constraints/Group.java @@ -0,0 +1,36 @@ +/* + * Copyright (C) 2015 higherfrequencytrading.com + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as published by + * the Free Software Foundation, either version 3 of the License. + * + * 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 Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with this program. If not, see <http://www.gnu.org/licenses/>. + */ + +package net.openhft.lang.model.constraints; + +import java.lang.annotation.Documented; +import java.lang.annotation.Retention; +import java.lang.annotation.Target; + +import static java.lang.annotation.ElementType.METHOD; +import static java.lang.annotation.RetentionPolicy.RUNTIME; + +/** + * Changes the serialization order fields. + * Multiple fields can be in the same group and these will be ordered with the smallest first. + * If you don't provided a group this is considered to be the top most group. + */ +@Target(METHOD) +@Retention(RUNTIME) +@Documented +public @interface Group { + int value() default Integer.MIN_VALUE; +} diff --git a/lang/src/main/java/net/openhft/lang/model/constraints/MaxSize.java b/lang/src/main/java/net/openhft/lang/model/constraints/MaxSize.java index 4ca6c4a..9ec653f 100644 --- a/lang/src/main/java/net/openhft/lang/model/constraints/MaxSize.java +++ b/lang/src/main/java/net/openhft/lang/model/constraints/MaxSize.java @@ -1,17 +1,17 @@ /* - * Copyright 2013 Peter Lawrey + * Copyright (C) 2015 higherfrequencytrading.com * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as published by + * the Free Software Foundation, either version 3 of the License. * - * http://www.apache.org/licenses/LICENSE-2.0 + * 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 Lesser General Public License for more details. * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. + * You should have received a copy of the GNU Lesser General Public License + * along with this program. If not, see <http://www.gnu.org/licenses/>. */ package net.openhft.lang.model.constraints; diff --git a/lang/src/main/java/net/openhft/lang/model/constraints/NotNull.java b/lang/src/main/java/net/openhft/lang/model/constraints/NotNull.java new file mode 100644 index 0000000..7706695 --- /dev/null +++ b/lang/src/main/java/net/openhft/lang/model/constraints/NotNull.java @@ -0,0 +1,30 @@ +/* + * Copyright (C) 2015 higherfrequencytrading.com + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as published by + * the Free Software Foundation, either version 3 of the License. + * + * 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 Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with this program. If not, see <http://www.gnu.org/licenses/>. + */ + +package net.openhft.lang.model.constraints; + +import java.lang.annotation.Documented; +import java.lang.annotation.Retention; +import java.lang.annotation.Target; + +import static java.lang.annotation.ElementType.*; +import static java.lang.annotation.RetentionPolicy.CLASS; + +@Documented +@Retention(CLASS) +@Target({METHOD, FIELD, PARAMETER, LOCAL_VARIABLE}) +public @interface NotNull { +} diff --git a/lang/src/main/java/net/openhft/lang/model/constraints/Nullable.java b/lang/src/main/java/net/openhft/lang/model/constraints/Nullable.java new file mode 100644 index 0000000..e3dd1ed --- /dev/null +++ b/lang/src/main/java/net/openhft/lang/model/constraints/Nullable.java @@ -0,0 +1,30 @@ +/* + * Copyright (C) 2015 higherfrequencytrading.com + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as published by + * the Free Software Foundation, either version 3 of the License. + * + * 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 Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with this program. If not, see <http://www.gnu.org/licenses/>. + */ + +package net.openhft.lang.model.constraints; + +import java.lang.annotation.Documented; +import java.lang.annotation.Retention; +import java.lang.annotation.Target; + +import static java.lang.annotation.ElementType.*; +import static java.lang.annotation.RetentionPolicy.CLASS; + +@Documented +@Retention(CLASS) +@Target({METHOD, FIELD, PARAMETER, LOCAL_VARIABLE}) +public @interface Nullable { +} diff --git a/lang/src/main/java/net/openhft/lang/model/constraints/Range.java b/lang/src/main/java/net/openhft/lang/model/constraints/Range.java index 2e99420..ddc2516 100644 --- a/lang/src/main/java/net/openhft/lang/model/constraints/Range.java +++ b/lang/src/main/java/net/openhft/lang/model/constraints/Range.java @@ -1,17 +1,17 @@ /* - * Copyright 2013 Peter Lawrey + * Copyright (C) 2015 higherfrequencytrading.com * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as published by + * the Free Software Foundation, either version 3 of the License. * - * http://www.apache.org/licenses/LICENSE-2.0 + * 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 Lesser General Public License for more details. * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. + * You should have received a copy of the GNU Lesser General Public License + * along with this program. If not, see <http://www.gnu.org/licenses/>. */ package net.openhft.lang.model.constraints; diff --git a/lang/src/main/java/net/openhft/lang/pool/CharSequenceInterner.java b/lang/src/main/java/net/openhft/lang/pool/CharSequenceInterner.java new file mode 100644 index 0000000..9c159fa --- /dev/null +++ b/lang/src/main/java/net/openhft/lang/pool/CharSequenceInterner.java @@ -0,0 +1,24 @@ +/* + * Copyright (C) 2015 higherfrequencytrading.com + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as published by + * the Free Software Foundation, either version 3 of the License. + * + * 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 Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with this program. If not, see <http://www.gnu.org/licenses/>. + */ + +package net.openhft.lang.pool; + +import net.openhft.lang.model.constraints.NotNull; + +public interface CharSequenceInterner<S> { + @NotNull + S intern(@NotNull CharSequence cs); +} diff --git a/lang/src/main/java/net/openhft/lang/pool/EnumInterner.java b/lang/src/main/java/net/openhft/lang/pool/EnumInterner.java new file mode 100644 index 0000000..88ae332 --- /dev/null +++ b/lang/src/main/java/net/openhft/lang/pool/EnumInterner.java @@ -0,0 +1,70 @@ +/* + * Copyright (C) 2015 higherfrequencytrading.com + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as published by + * the Free Software Foundation, either version 3 of the License. + * + * 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 Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with this program. If not, see <http://www.gnu.org/licenses/>. + */ + +package net.openhft.lang.pool; + +import net.openhft.lang.Maths; +import net.openhft.lang.model.constraints.NotNull; +import net.openhft.lang.model.constraints.Nullable; + +/** + * @author peter.lawrey + */ +public class EnumInterner<E extends Enum<E>> implements CharSequenceInterner<Enum<E>> { + private static final ClassValue<EnumInterner> internerForClass = new ClassValue<EnumInterner>() { + @Override + protected EnumInterner computeValue(Class<?> type) { + return new EnumInterner(type); + } + }; + private final Enum<E>[] interner; + private final int mask; + private final Class<E> enumType; + + private EnumInterner(Class<E> enumType) { + this.enumType = enumType; + int n = 128; + interner = (Enum<E>[]) new Enum[n]; + mask = n - 1; + } + + public static <T extends Enum<T>> T intern(Class<T> enumType, CharSequence cs) { + return (T) internerForClass.get(enumType).intern(cs); + } + + public static boolean isEqual(@Nullable CharSequence s, @NotNull CharSequence cs) { + if (s == null) return false; + if (s.length() != cs.length()) return false; + for (int i = 0; i < cs.length(); i++) + if (s.charAt(i) != cs.charAt(i)) + return false; + return true; + } + + @Override + @NotNull + public Enum<E> intern(@NotNull CharSequence cs) { + long hash = 0; + for (int i = 0; i < cs.length(); i++) + hash = 57 * hash + cs.charAt(i); + int h = (int) Maths.hash(hash) & mask; + Enum<E> e = interner[h]; + if (e != null && isEqual(e.name(), cs)) + return e; + String s2 = cs.toString(); + return interner[h] = Enum.valueOf(enumType, s2); + } +} diff --git a/lang/src/main/java/net/openhft/lang/pool/StringInterner.java b/lang/src/main/java/net/openhft/lang/pool/StringInterner.java index 3796bc0..b8e2501 100755..100644 --- a/lang/src/main/java/net/openhft/lang/pool/StringInterner.java +++ b/lang/src/main/java/net/openhft/lang/pool/StringInterner.java @@ -1,30 +1,29 @@ /* - * Copyright 2013 Peter Lawrey + * Copyright (C) 2015 higherfrequencytrading.com * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as published by + * the Free Software Foundation, either version 3 of the License. * - * http://www.apache.org/licenses/LICENSE-2.0 + * 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 Lesser General Public License for more details. * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. + * You should have received a copy of the GNU Lesser General Public License + * along with this program. If not, see <http://www.gnu.org/licenses/>. */ package net.openhft.lang.pool; import net.openhft.lang.Maths; -import org.jetbrains.annotations.NotNull; -import org.jetbrains.annotations.Nullable; +import net.openhft.lang.model.constraints.NotNull; +import net.openhft.lang.model.constraints.Nullable; /** * @author peter.lawrey */ -public class StringInterner { - @NotNull +public class StringInterner implements CharSequenceInterner<String> { private final String[] interner; private final int mask; @@ -34,7 +33,7 @@ public class StringInterner { mask = n - 1; } - private static boolean isEqual(@Nullable CharSequence s, @NotNull CharSequence cs) { + public static boolean isEqual(@Nullable CharSequence s, @NotNull CharSequence cs) { if (s == null) return false; if (s.length() != cs.length()) return false; for (int i = 0; i < cs.length(); i++) @@ -43,32 +42,13 @@ public class StringInterner { return true; } -/* private static boolean isEqual(@Nullable CharSequence s, @NotNull byte[] bytes, int off, int len) { - if (s == null) return false; - if (s.length() != len) return false; - for (int i = 0; i < len; i++) - if (s.charAt(i) != (bytes[off + i] & 0xFF)) - return false; - return true; - } - - @NotNull - public String intern(@NotNull byte[] bytes, int off, int len) { - long hash = NativeBytes.longHash(bytes, off, len); - int h = Maths.hash(hash) & mask; - String s = interner[h]; - if (isEqual(s, bytes, off, len)) - return s; - String s2 = new String(bytes, off, len, IOTools.ISO_8859_1); - return interner[h] = s2; - }*/ - + @Override @NotNull public String intern(@NotNull CharSequence cs) { long hash = 0; for (int i = 0; i < cs.length(); i++) hash = 57 * hash + cs.charAt(i); - int h = Maths.hash(hash) & mask; + int h = (int) Maths.hash(hash) & mask; String s = interner[h]; if (isEqual(s, cs)) return s; diff --git a/lang/src/main/java/net/openhft/lang/testing/Differencer.java b/lang/src/main/java/net/openhft/lang/testing/Differencer.java index 29d44f1..5fcae8b 100644 --- a/lang/src/main/java/net/openhft/lang/testing/Differencer.java +++ b/lang/src/main/java/net/openhft/lang/testing/Differencer.java @@ -1,17 +1,17 @@ /* - * Copyright 2013 Peter Lawrey + * Copyright (C) 2015 higherfrequencytrading.com * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as published by + * the Free Software Foundation, either version 3 of the License. * - * http://www.apache.org/licenses/LICENSE-2.0 + * 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 Lesser General Public License for more details. * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. + * You should have received a copy of the GNU Lesser General Public License + * along with this program. If not, see <http://www.gnu.org/licenses/>. */ package net.openhft.lang.testing; @@ -21,6 +21,6 @@ package net.openhft.lang.testing; * Date: 05/08/13 * Time: 19:06 */ -public interface Differencer { +interface Differencer { long sample(long startTime, long endTime); } diff --git a/lang/src/main/java/net/openhft/lang/testing/RunningMinimum.java b/lang/src/main/java/net/openhft/lang/testing/RunningMinimum.java index 983f114..8b3e59b 100644 --- a/lang/src/main/java/net/openhft/lang/testing/RunningMinimum.java +++ b/lang/src/main/java/net/openhft/lang/testing/RunningMinimum.java @@ -1,17 +1,17 @@ /* - * Copyright 2013 Peter Lawrey + * Copyright (C) 2015 higherfrequencytrading.com * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as published by + * the Free Software Foundation, either version 3 of the License. * - * http://www.apache.org/licenses/LICENSE-2.0 + * 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 Lesser General Public License for more details. * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. + * You should have received a copy of the GNU Lesser General Public License + * along with this program. If not, see <http://www.gnu.org/licenses/>. */ package net.openhft.lang.testing; @@ -31,7 +31,7 @@ public class RunningMinimum implements Differencer { this(actualMinimum, 100 * 1000); } - public RunningMinimum(long actualMinimum, int drift) { + private RunningMinimum(long actualMinimum, int drift) { this.actualMinimum = actualMinimum; this.drift = drift; } diff --git a/lang/src/main/java/net/openhft/lang/testing/VanillaDifferencer.java b/lang/src/main/java/net/openhft/lang/testing/VanillaDifferencer.java index 3853861..a14a934 100644 --- a/lang/src/main/java/net/openhft/lang/testing/VanillaDifferencer.java +++ b/lang/src/main/java/net/openhft/lang/testing/VanillaDifferencer.java @@ -1,17 +1,17 @@ /* - * Copyright 2013 Peter Lawrey + * Copyright (C) 2015 higherfrequencytrading.com * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as published by + * the Free Software Foundation, either version 3 of the License. * - * http://www.apache.org/licenses/LICENSE-2.0 + * 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 Lesser General Public License for more details. * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. + * You should have received a copy of the GNU Lesser General Public License + * along with this program. If not, see <http://www.gnu.org/licenses/>. */ package net.openhft.lang.testing; diff --git a/lang/src/main/java/net/openhft/lang/thread/BusyPauser.java b/lang/src/main/java/net/openhft/lang/thread/BusyPauser.java new file mode 100755 index 0000000..ad5872c --- /dev/null +++ b/lang/src/main/java/net/openhft/lang/thread/BusyPauser.java @@ -0,0 +1,40 @@ +/* + * Copyright (C) 2015 higherfrequencytrading.com + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as published by + * the Free Software Foundation, either version 3 of the License. + * + * 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 Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with this program. If not, see <http://www.gnu.org/licenses/>. + */ + +package net.openhft.lang.thread; + +/** + * Created by peter.lawrey on 11/12/14. + */ +public enum BusyPauser implements Pauser { + INSTANCE; + + @Override + public void reset() { + } + + @Override + public void pause() { + } + + @Override + public void pause(long maxPauseNS) { + } + + @Override + public void unpause() { + } +} diff --git a/lang/src/main/java/net/openhft/lang/thread/LightPauser.java b/lang/src/main/java/net/openhft/lang/thread/LightPauser.java new file mode 100755 index 0000000..ae6142d --- /dev/null +++ b/lang/src/main/java/net/openhft/lang/thread/LightPauser.java @@ -0,0 +1,79 @@ +/* + * Copyright (C) 2015 higherfrequencytrading.com + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as published by + * the Free Software Foundation, either version 3 of the License. + * + * 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 Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with this program. If not, see <http://www.gnu.org/licenses/>. + */ + +package net.openhft.lang.thread; + +import java.util.concurrent.atomic.AtomicBoolean; +import java.util.concurrent.locks.LockSupport; + +/** + * Created by peter.lawrey on 11/12/14. + */ +public class LightPauser implements Pauser { + public static final long NO_BUSY_PERIOD = -1; + public static final long NO_PAUSE_PERIOD = -1; + private final AtomicBoolean pausing = new AtomicBoolean(); + private final long busyPeriodNS; + private final long parkPeriodNS; + private int count; + private long pauseStart = 0; + private volatile Thread thread; + + public LightPauser(long busyPeriodNS, long parkPeriodNS) { + this.busyPeriodNS = busyPeriodNS; + this.parkPeriodNS = parkPeriodNS; + } + + @Override + public void reset() { + pauseStart = count = 0; + } + + @Override + public void pause() { + pause(parkPeriodNS); + } + + public void pause(long maxPauseNS) { + if (busyPeriodNS > 0) { + if (count++ < 1000) + return; + if (pauseStart == 0) { + pauseStart = System.nanoTime(); + return; + } + if (System.nanoTime() < pauseStart + busyPeriodNS) + return; + } + if (maxPauseNS < 10000) + return; + thread = Thread.currentThread(); + pausing.set(true); + doPause(maxPauseNS); + pausing.set(false); + reset(); + } + + protected void doPause(long maxPauseNS) { + LockSupport.parkNanos(Math.max(maxPauseNS, parkPeriodNS)); + } + + @Override + public void unpause() { + if (pausing.get()) + LockSupport.unpark(thread); + } +} diff --git a/lang/src/main/java/net/openhft/lang/thread/NamedThreadFactory.java b/lang/src/main/java/net/openhft/lang/thread/NamedThreadFactory.java index fc9895e..5c8c7a7 100644 --- a/lang/src/main/java/net/openhft/lang/thread/NamedThreadFactory.java +++ b/lang/src/main/java/net/openhft/lang/thread/NamedThreadFactory.java @@ -1,22 +1,22 @@ /* - * Copyright 2013 Peter Lawrey + * Copyright (C) 2015 higherfrequencytrading.com * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as published by + * the Free Software Foundation, either version 3 of the License. * - * http://www.apache.org/licenses/LICENSE-2.0 + * 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 Lesser General Public License for more details. * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. + * You should have received a copy of the GNU Lesser General Public License + * along with this program. If not, see <http://www.gnu.org/licenses/>. */ package net.openhft.lang.thread; -import org.jetbrains.annotations.NotNull; +import net.openhft.lang.model.constraints.NotNull; import java.util.concurrent.ThreadFactory; import java.util.concurrent.atomic.AtomicInteger; diff --git a/lang/src/main/java/net/openhft/lang/thread/Pauser.java b/lang/src/main/java/net/openhft/lang/thread/Pauser.java new file mode 100755 index 0000000..13f954c --- /dev/null +++ b/lang/src/main/java/net/openhft/lang/thread/Pauser.java @@ -0,0 +1,30 @@ +/* + * Copyright (C) 2015 higherfrequencytrading.com + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as published by + * the Free Software Foundation, either version 3 of the License. + * + * 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 Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with this program. If not, see <http://www.gnu.org/licenses/>. + */ + +package net.openhft.lang.thread; + +/** + * Created by peter.lawrey on 11/12/14. + */ +public interface Pauser { + void reset(); + + void pause(); + + void pause(long maxPauseNS); + + void unpause(); +} diff --git a/lang/src/main/java/net/openhft/lang/threadlocal/Provider.java b/lang/src/main/java/net/openhft/lang/threadlocal/Provider.java new file mode 100644 index 0000000..ce9de09 --- /dev/null +++ b/lang/src/main/java/net/openhft/lang/threadlocal/Provider.java @@ -0,0 +1,96 @@ +/* + * Copyright (C) 2015 higherfrequencytrading.com + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as published by + * the Free Software Foundation, either version 3 of the License. + * + * 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 Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with this program. If not, see <http://www.gnu.org/licenses/>. + */ + +package net.openhft.lang.threadlocal; + +public abstract class Provider<T> { + + public static <T> Provider<T> of(Class<T> tClass) { + if (StatefulCopyable.class.isAssignableFrom(tClass)) { + return StatefulProvider.INSTANCE; + } + return StatelessProvider.INSTANCE; + } + + public abstract T get(ThreadLocalCopies copies, T original); + + public abstract ThreadLocalCopies getCopies(ThreadLocalCopies copies); + + private static final class StatefulProvider<T extends StatefulCopyable<T>> extends Provider<T> { + private static final Provider INSTANCE = new StatefulProvider(); + + @Override + public ThreadLocalCopies getCopies(ThreadLocalCopies copies) { + if (copies != null) + return copies; + return ThreadLocalCopies.get(); + } + + @Override + public T get(ThreadLocalCopies copies, T original) { + return get(copies, original, true); + } + + private T get(ThreadLocalCopies copies, T original, boolean syncPut) { + Object id = original.stateIdentity(); + int m = copies.mask; + Object[] tab = copies.table; + int i = System.identityHashCode(id) & m; + while (true) { + Object idInTable = tab[i]; + if (idInTable == id) { + return (T) tab[i + 1]; + + } else if (idInTable == null) { + if (syncPut) { + if (copies.currentlyAccessed.compareAndSet(false, true)) { + try { + return get(copies, original, false); + } finally { + copies.currentlyAccessed.set(false); + } + } else { + throw new IllegalStateException("Concurrent or recursive access " + + "to ThreadLocalCopies is not allowed"); + } + } else { + // actual put + tab[i] = id; + T copy; + tab[i + 1] = copy = original.copy(); + copies.postInsert(); + return copy; + } + } + i = (i + 2) & m; + } + } + } + + private static final class StatelessProvider<M> extends Provider<M> { + private static final Provider INSTANCE = new StatelessProvider(); + + @Override + public M get(ThreadLocalCopies copies, M original) { + return original; + } + + @Override + public ThreadLocalCopies getCopies(ThreadLocalCopies copies) { + return copies; + } + } +} diff --git a/lang/src/main/java/net/openhft/lang/threadlocal/StatefulCopyable.java b/lang/src/main/java/net/openhft/lang/threadlocal/StatefulCopyable.java new file mode 100644 index 0000000..65f6371 --- /dev/null +++ b/lang/src/main/java/net/openhft/lang/threadlocal/StatefulCopyable.java @@ -0,0 +1,23 @@ +/* + * Copyright (C) 2015 higherfrequencytrading.com + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as published by + * the Free Software Foundation, either version 3 of the License. + * + * 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 Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with this program. If not, see <http://www.gnu.org/licenses/>. + */ + +package net.openhft.lang.threadlocal; + +public interface StatefulCopyable<S extends StatefulCopyable<S>> { + Object stateIdentity(); + + S copy(); +} diff --git a/lang/src/main/java/net/openhft/lang/threadlocal/ThreadLocalCopies.java b/lang/src/main/java/net/openhft/lang/threadlocal/ThreadLocalCopies.java new file mode 100644 index 0000000..7d949ea --- /dev/null +++ b/lang/src/main/java/net/openhft/lang/threadlocal/ThreadLocalCopies.java @@ -0,0 +1,73 @@ +/* + * Copyright (C) 2015 higherfrequencytrading.com + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as published by + * the Free Software Foundation, either version 3 of the License. + * + * 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 Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with this program. If not, see <http://www.gnu.org/licenses/>. + */ + +package net.openhft.lang.threadlocal; + +import java.util.concurrent.atomic.AtomicBoolean; + +public final class ThreadLocalCopies { + + private static ThreadLocal<ThreadLocalCopies> states = new ThreadLocal<ThreadLocalCopies>() { + @Override + protected ThreadLocalCopies initialValue() { + return new ThreadLocalCopies(); + } + }; + + public static ThreadLocalCopies get() { + return states.get(); + } + + final AtomicBoolean currentlyAccessed = new AtomicBoolean(false); + Object[] table; + int size = 0, sizeLimit, mask; + + public ThreadLocalCopies() { + init(32); // 16 entries + } + + void init(int doubledCapacity) { + table = new Object[doubledCapacity]; + sizeLimit = doubledCapacity / 3; // 0.66 fullness + mask = (doubledCapacity - 1) & ~1; + } + + void rehash() { + Object[] oldTab = this.table; + int oldCapacity = oldTab.length; + if (oldCapacity == (1 << 30)) + throw new IllegalStateException("Hash is full"); + init(oldCapacity << 1); + int m = mask; + Object[] tab = this.table; + for (int oldI = 0; oldI < oldCapacity; oldI += 2) { + Object id = oldTab[oldI]; + if (id != null) { + int i = System.identityHashCode(id) & m; + while (tab[i] != null) { + i = (i + 2) & m; + } + tab[i] = id; + tab[i + 1] = oldTab[oldI + 1]; + } + } + } + + void postInsert() { + if (++size > sizeLimit) + rehash(); + } +} diff --git a/lang/src/main/java/net/openhft/lang/values/BooleanValue.java b/lang/src/main/java/net/openhft/lang/values/BooleanValue.java index 5af319c..13d42e3 100644 --- a/lang/src/main/java/net/openhft/lang/values/BooleanValue.java +++ b/lang/src/main/java/net/openhft/lang/values/BooleanValue.java @@ -1,17 +1,17 @@ /* - * Copyright 2013 Peter Lawrey + * Copyright (C) 2015 higherfrequencytrading.com * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as published by + * the Free Software Foundation, either version 3 of the License. * - * http://www.apache.org/licenses/LICENSE-2.0 + * 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 Lesser General Public License for more details. * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. + * You should have received a copy of the GNU Lesser General Public License + * along with this program. If not, see <http://www.gnu.org/licenses/>. */ package net.openhft.lang.values; diff --git a/lang/src/main/java/net/openhft/lang/values/ByteValue.java b/lang/src/main/java/net/openhft/lang/values/ByteValue.java index b0d1f62..c281ad5 100644 --- a/lang/src/main/java/net/openhft/lang/values/ByteValue.java +++ b/lang/src/main/java/net/openhft/lang/values/ByteValue.java @@ -1,17 +1,17 @@ /* - * Copyright 2013 Peter Lawrey + * Copyright (C) 2015 higherfrequencytrading.com * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as published by + * the Free Software Foundation, either version 3 of the License. * - * http://www.apache.org/licenses/LICENSE-2.0 + * 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 Lesser General Public License for more details. * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. + * You should have received a copy of the GNU Lesser General Public License + * along with this program. If not, see <http://www.gnu.org/licenses/>. */ package net.openhft.lang.values; diff --git a/lang/src/main/java/net/openhft/lang/values/CharValue.java b/lang/src/main/java/net/openhft/lang/values/CharValue.java index d798208..84cc59a 100644 --- a/lang/src/main/java/net/openhft/lang/values/CharValue.java +++ b/lang/src/main/java/net/openhft/lang/values/CharValue.java @@ -1,17 +1,17 @@ /* - * Copyright 2013 Peter Lawrey + * Copyright (C) 2015 higherfrequencytrading.com * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as published by + * the Free Software Foundation, either version 3 of the License. * - * http://www.apache.org/licenses/LICENSE-2.0 + * 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 Lesser General Public License for more details. * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. + * You should have received a copy of the GNU Lesser General Public License + * along with this program. If not, see <http://www.gnu.org/licenses/>. */ package net.openhft.lang.values; diff --git a/lang/src/main/java/net/openhft/lang/values/DoubleValue.java b/lang/src/main/java/net/openhft/lang/values/DoubleValue.java index 8954f73..61ca9d2 100644 --- a/lang/src/main/java/net/openhft/lang/values/DoubleValue.java +++ b/lang/src/main/java/net/openhft/lang/values/DoubleValue.java @@ -1,17 +1,17 @@ /* - * Copyright 2013 Peter Lawrey + * Copyright (C) 2015 higherfrequencytrading.com * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as published by + * the Free Software Foundation, either version 3 of the License. * - * http://www.apache.org/licenses/LICENSE-2.0 + * 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 Lesser General Public License for more details. * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. + * You should have received a copy of the GNU Lesser General Public License + * along with this program. If not, see <http://www.gnu.org/licenses/>. */ package net.openhft.lang.values; @@ -26,6 +26,10 @@ public interface DoubleValue { void setValue(double value); + double getVolatileValue(); + + void setOrderedValue(double value); + double addValue(double delta); double addAtomicValue(double delta); diff --git a/lang/src/main/java/net/openhft/lang/values/FloatValue.java b/lang/src/main/java/net/openhft/lang/values/FloatValue.java index b56152b..1890b48 100644 --- a/lang/src/main/java/net/openhft/lang/values/FloatValue.java +++ b/lang/src/main/java/net/openhft/lang/values/FloatValue.java @@ -1,17 +1,17 @@ /* - * Copyright 2013 Peter Lawrey + * Copyright (C) 2015 higherfrequencytrading.com * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as published by + * the Free Software Foundation, either version 3 of the License. * - * http://www.apache.org/licenses/LICENSE-2.0 + * 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 Lesser General Public License for more details. * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. + * You should have received a copy of the GNU Lesser General Public License + * along with this program. If not, see <http://www.gnu.org/licenses/>. */ package net.openhft.lang.values; @@ -26,6 +26,10 @@ public interface FloatValue { void setValue(float value); + void setOrderedValue(float value); + + float getVolatileValue(); + float addValue(float delta); float addAtomicValue(float delta); diff --git a/lang/src/main/java/net/openhft/lang/values/Int24Value.java b/lang/src/main/java/net/openhft/lang/values/Int24Value.java index 538ea42..21e1e6c 100644 --- a/lang/src/main/java/net/openhft/lang/values/Int24Value.java +++ b/lang/src/main/java/net/openhft/lang/values/Int24Value.java @@ -1,17 +1,17 @@ /* - * Copyright 2013 Peter Lawrey + * Copyright (C) 2015 higherfrequencytrading.com * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as published by + * the Free Software Foundation, either version 3 of the License. * - * http://www.apache.org/licenses/LICENSE-2.0 + * 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 Lesser General Public License for more details. * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. + * You should have received a copy of the GNU Lesser General Public License + * along with this program. If not, see <http://www.gnu.org/licenses/>. */ package net.openhft.lang.values; diff --git a/lang/src/main/java/net/openhft/lang/values/Int48Value.java b/lang/src/main/java/net/openhft/lang/values/Int48Value.java index bb7482b..1a17216 100644 --- a/lang/src/main/java/net/openhft/lang/values/Int48Value.java +++ b/lang/src/main/java/net/openhft/lang/values/Int48Value.java @@ -1,17 +1,17 @@ /* - * Copyright 2013 Peter Lawrey + * Copyright (C) 2015 higherfrequencytrading.com * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as published by + * the Free Software Foundation, either version 3 of the License. * - * http://www.apache.org/licenses/LICENSE-2.0 + * 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 Lesser General Public License for more details. * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. + * You should have received a copy of the GNU Lesser General Public License + * along with this program. If not, see <http://www.gnu.org/licenses/>. */ package net.openhft.lang.values; diff --git a/lang/src/main/java/net/openhft/lang/values/IntValue.java b/lang/src/main/java/net/openhft/lang/values/IntValue.java index 892d640..685c253 100644 --- a/lang/src/main/java/net/openhft/lang/values/IntValue.java +++ b/lang/src/main/java/net/openhft/lang/values/IntValue.java @@ -1,17 +1,17 @@ /* - * Copyright 2013 Peter Lawrey + * Copyright (C) 2015 higherfrequencytrading.com * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as published by + * the Free Software Foundation, either version 3 of the License. * - * http://www.apache.org/licenses/LICENSE-2.0 + * 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 Lesser General Public License for more details. * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. + * You should have received a copy of the GNU Lesser General Public License + * along with this program. If not, see <http://www.gnu.org/licenses/>. */ package net.openhft.lang.values; @@ -26,6 +26,10 @@ public interface IntValue { void setValue(int value); + int getVolatileValue(); + + void setOrderedValue(int value); + int addValue(int delta); int addAtomicValue(int delta); @@ -36,8 +40,7 @@ public interface IntValue { boolean tryLockNanosValue(long nanos); - void busyLockValue() throws InterruptedException, IllegalStateException; + void busyLockValue() throws IllegalStateException, InterruptedException; void unlockValue() throws IllegalMonitorStateException; - } diff --git a/lang/src/main/java/net/openhft/lang/values/LongValue.java b/lang/src/main/java/net/openhft/lang/values/LongValue.java index b59b707..f015940 100644 --- a/lang/src/main/java/net/openhft/lang/values/LongValue.java +++ b/lang/src/main/java/net/openhft/lang/values/LongValue.java @@ -1,17 +1,17 @@ /* - * Copyright 2013 Peter Lawrey + * Copyright (C) 2015 higherfrequencytrading.com * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as published by + * the Free Software Foundation, either version 3 of the License. * - * http://www.apache.org/licenses/LICENSE-2.0 + * 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 Lesser General Public License for more details. * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. + * You should have received a copy of the GNU Lesser General Public License + * along with this program. If not, see <http://www.gnu.org/licenses/>. */ package net.openhft.lang.values; @@ -26,6 +26,10 @@ public interface LongValue { void setValue(long value); + long getVolatileValue(); + + void setOrderedValue(long value); + long addValue(long delta); long addAtomicValue(long delta); diff --git a/lang/src/main/java/net/openhft/lang/values/ShortValue.java b/lang/src/main/java/net/openhft/lang/values/ShortValue.java index 4dd5547..1b859b3 100644 --- a/lang/src/main/java/net/openhft/lang/values/ShortValue.java +++ b/lang/src/main/java/net/openhft/lang/values/ShortValue.java @@ -1,17 +1,17 @@ /* - * Copyright 2013 Peter Lawrey + * Copyright (C) 2015 higherfrequencytrading.com * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as published by + * the Free Software Foundation, either version 3 of the License. * - * http://www.apache.org/licenses/LICENSE-2.0 + * 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 Lesser General Public License for more details. * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. + * You should have received a copy of the GNU Lesser General Public License + * along with this program. If not, see <http://www.gnu.org/licenses/>. */ package net.openhft.lang.values; diff --git a/lang/src/main/java/net/openhft/lang/values/StringValue.java b/lang/src/main/java/net/openhft/lang/values/StringValue.java index 2a0c587..e068730 100644 --- a/lang/src/main/java/net/openhft/lang/values/StringValue.java +++ b/lang/src/main/java/net/openhft/lang/values/StringValue.java @@ -1,17 +1,17 @@ /* - * Copyright 2013 Peter Lawrey + * Copyright (C) 2015 higherfrequencytrading.com * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as published by + * the Free Software Foundation, either version 3 of the License. * - * http://www.apache.org/licenses/LICENSE-2.0 + * 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 Lesser General Public License for more details. * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. + * You should have received a copy of the GNU Lesser General Public License + * along with this program. If not, see <http://www.gnu.org/licenses/>. */ package net.openhft.lang.values; @@ -19,12 +19,18 @@ package net.openhft.lang.values; import net.openhft.lang.model.constraints.MaxSize; /** - * User: peter.lawrey - * Date: 10/10/13 - * Time: 07:13 + * User: peter.lawrey Date: 10/10/13 Time: 07:13 */ public interface StringValue { + String getValue(); void setValue(@MaxSize CharSequence value); + + /** + * a getter for a String which takes a StringBuilder + * @param stringBuilder the builder to return + * @return a StringBuilder containing the value + */ + StringBuilder getUsingValue(StringBuilder stringBuilder); } diff --git a/lang/src/main/java/net/openhft/lang/values/UnsignedByteValue.java b/lang/src/main/java/net/openhft/lang/values/UnsignedByteValue.java index 7cc5007..8a61991 100644 --- a/lang/src/main/java/net/openhft/lang/values/UnsignedByteValue.java +++ b/lang/src/main/java/net/openhft/lang/values/UnsignedByteValue.java @@ -1,17 +1,17 @@ /* - * Copyright 2013 Peter Lawrey + * Copyright (C) 2015 higherfrequencytrading.com * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as published by + * the Free Software Foundation, either version 3 of the License. * - * http://www.apache.org/licenses/LICENSE-2.0 + * 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 Lesser General Public License for more details. * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. + * You should have received a copy of the GNU Lesser General Public License + * along with this program. If not, see <http://www.gnu.org/licenses/>. */ package net.openhft.lang.values; diff --git a/lang/src/main/java/net/openhft/lang/values/UnsignedIntValue.java b/lang/src/main/java/net/openhft/lang/values/UnsignedIntValue.java index 8eae1a3..3b4d96f 100644 --- a/lang/src/main/java/net/openhft/lang/values/UnsignedIntValue.java +++ b/lang/src/main/java/net/openhft/lang/values/UnsignedIntValue.java @@ -1,17 +1,17 @@ /* - * Copyright 2013 Peter Lawrey + * Copyright (C) 2015 higherfrequencytrading.com * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as published by + * the Free Software Foundation, either version 3 of the License. * - * http://www.apache.org/licenses/LICENSE-2.0 + * 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 Lesser General Public License for more details. * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. + * You should have received a copy of the GNU Lesser General Public License + * along with this program. If not, see <http://www.gnu.org/licenses/>. */ package net.openhft.lang.values; @@ -29,5 +29,4 @@ public interface UnsignedIntValue { void setValue(@Range(min = 0, max = (1L << 32) - 1) long value); long addValue(long delta); - } diff --git a/lang/src/main/java/net/openhft/lang/values/UnsignedShortValue.java b/lang/src/main/java/net/openhft/lang/values/UnsignedShortValue.java index b9d06d6..a82661b 100644 --- a/lang/src/main/java/net/openhft/lang/values/UnsignedShortValue.java +++ b/lang/src/main/java/net/openhft/lang/values/UnsignedShortValue.java @@ -1,17 +1,17 @@ /* - * Copyright 2013 Peter Lawrey + * Copyright (C) 2015 higherfrequencytrading.com * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as published by + * the Free Software Foundation, either version 3 of the License. * - * http://www.apache.org/licenses/LICENSE-2.0 + * 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 Lesser General Public License for more details. * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. + * You should have received a copy of the GNU Lesser General Public License + * along with this program. If not, see <http://www.gnu.org/licenses/>. */ package net.openhft.lang.values; diff --git a/lang/src/test/java/DataValueGroupTest$BaseInterface$$Native.java b/lang/src/test/java/DataValueGroupTest$BaseInterface$$Native.java new file mode 100644 index 0000000..29c7668 --- /dev/null +++ b/lang/src/test/java/DataValueGroupTest$BaseInterface$$Native.java @@ -0,0 +1,112 @@ +/* + * Copyright (C) 2015 higherfrequencytrading.com + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as published by + * the Free Software Foundation, either version 3 of the License. + * + * 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 Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with this program. If not, see <http://www.gnu.org/licenses/>. + */ + +package net.openhft.lang; + +import net.openhft.lang.io.Bytes; +import net.openhft.lang.io.serialization.BytesMarshallable; +import net.openhft.lang.model.Byteable; +import net.openhft.lang.model.Copyable; + +import static net.openhft.lang.Compare.calcLongHashCode; +import static net.openhft.lang.Compare.isEqual; + +public class DataValueGroupTest$BaseInterface$$Native implements GroupTest.BaseInterface, BytesMarshallable, Byteable, Copyable<GroupTest.BaseInterface> { + private static final int INT = 0; + private static final int STR = 4; + + private Bytes _bytes; + private long _offset; + + public void setInt(int $) { + _bytes.writeInt(_offset + INT, $); + } + + public int getInt() { + return _bytes.readInt(_offset + INT); + } + + public void setStr(java.lang.String $) { + _bytes.writeUTFΔ(_offset + STR, 15, $); + } + + public java.lang.String getStr() { + return _bytes.readUTFΔ(_offset + STR); + } + + @Override + public void copyFrom(GroupTest.BaseInterface from) { + setInt(from.getInt()); + setStr(from.getStr()); + } + + @Override + public void writeMarshallable(Bytes out) { + out.writeInt(getInt()); + out.writeUTFΔ(getStr()); + } + @Override + public void readMarshallable(Bytes in) { + setInt(in.readInt()); + setStr(in.readUTFΔ()); + } + @Override + public void bytes(Bytes bytes, long offset) { + this._bytes = bytes; + this._offset = offset; + } + @Override + public Bytes bytes() { + return _bytes; + } + @Override + public long offset() { + return _offset; + } + @Override + public int maxSize() { + return 19; + } + public int hashCode() { + long lhc = longHashCode(); + return (int) ((lhc >>> 32) ^ lhc); + } + + public long longHashCode() { + return (calcLongHashCode(getInt())) * 10191 + + calcLongHashCode(getStr()); + } + + public boolean equals(Object o) { + if (this == o) return true; + if (!(o instanceof GroupTest.BaseInterface)) return false; + GroupTest.BaseInterface that = (GroupTest.BaseInterface) o; + + if(!isEqual(getInt(), that.getInt())) return false; + return isEqual(getStr(), that.getStr()); + } + + public String toString() { + if (_bytes == null) return "bytes is null"; + StringBuilder sb = new StringBuilder(); + sb.append("DataValueGroupTest.BaseInterface{ "); + sb.append("int= ").append(getInt()); +sb.append(", ") +; sb.append("str= ").append(getStr()); + sb.append(" }"); + return sb.toString(); + } +} diff --git a/lang/src/test/java/net/openhft/lang/GroupTest.java b/lang/src/test/java/net/openhft/lang/GroupTest.java new file mode 100644 index 0000000..10f0862 --- /dev/null +++ b/lang/src/test/java/net/openhft/lang/GroupTest.java @@ -0,0 +1,111 @@ +/* + * Copyright (C) 2015 higherfrequencytrading.com + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as published by + * the Free Software Foundation, either version 3 of the License. + * + * 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 Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with this program. If not, see <http://www.gnu.org/licenses/>. + */ + +package net.openhft.lang; + +import net.openhft.lang.io.ByteBufferBytes; +import net.openhft.lang.io.IByteBufferBytes; +import net.openhft.lang.model.Byteable; +import net.openhft.lang.model.DataValueClasses; +import net.openhft.lang.model.constraints.Group; +import net.openhft.lang.model.constraints.MaxSize; +import org.junit.Assert; +import org.junit.Test; + +import java.nio.ByteBuffer; + +/** + * Rob Austin + */ +public class GroupTest { + + @Test + public void test() { + IByteBufferBytes byteBufferBytes = ByteBufferBytes.wrap(ByteBuffer.allocate(1024)); + + { + BaseInterface baseInterface = DataValueClasses.newDirectReference(BaseInterface.class); + + ((Byteable) baseInterface).bytes(byteBufferBytes, 0); + + baseInterface.setInt(1); + baseInterface.setStr("Hello World"); + + Assert.assertEquals(1, baseInterface.getInt()); + Assert.assertEquals("Hello World", baseInterface.getStr()); + } + { + ExtInterface extInterface = DataValueClasses.newDirectReference(ExtInterface.class); + byteBufferBytes.clear(); + ((Byteable) extInterface).bytes(byteBufferBytes, 0); + extInterface.setInt2(43); + + Assert.assertEquals(1, extInterface.getInt()); + Assert.assertEquals(43, extInterface.getInt2()); + Assert.assertEquals("Hello World", extInterface.getStr()); + extInterface.setInt(2); + + Assert.assertEquals(2, extInterface.getInt()); + } + } + + @Test + public void test2() { + IByteBufferBytes byteBufferBytes = ByteBufferBytes.wrap(ByteBuffer.allocate(1024)); + + { + ExtInterface extInterface = DataValueClasses.newDirectReference(ExtInterface.class); + byteBufferBytes.clear(); + ((Byteable) extInterface).bytes(byteBufferBytes, 0); + + extInterface.setInt(1); + extInterface.setInt2(2); + extInterface.setStr("Hello World"); + + Assert.assertEquals(1, extInterface.getInt()); + Assert.assertEquals(1, extInterface.getInt()); + Assert.assertEquals("Hello World", extInterface.getStr()); + } + + { + BaseInterface baseInterface = DataValueClasses.newDirectReference(BaseInterface.class); + byteBufferBytes.clear(); + ((Byteable) baseInterface).bytes(byteBufferBytes, 0); + + Assert.assertEquals(1, baseInterface.getInt()); + Assert.assertEquals("Hello World", baseInterface.getStr()); + } + } + + public interface BaseInterface { + + String getStr(); + + void setStr(@MaxSize(15) String str); + + int getInt(); + + void setInt(int i); + } + + public interface ExtInterface extends BaseInterface { + + int getInt2(); + + @Group(1) + void setInt2(int i); + } +} diff --git a/lang/src/test/java/net/openhft/lang/JvmTest.java b/lang/src/test/java/net/openhft/lang/JvmTest.java index af3eae8..3ce24e7 100644 --- a/lang/src/test/java/net/openhft/lang/JvmTest.java +++ b/lang/src/test/java/net/openhft/lang/JvmTest.java @@ -1,17 +1,17 @@ /* - * Copyright 2013 Peter Lawrey + * Copyright (C) 2015 higherfrequencytrading.com * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as published by + * the Free Software Foundation, either version 3 of the License. * - * http://www.apache.org/licenses/LICENSE-2.0 + * 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 Lesser General Public License for more details. * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. + * You should have received a copy of the GNU Lesser General Public License + * along with this program. If not, see <http://www.gnu.org/licenses/>. */ package net.openhft.lang; diff --git a/lang/src/test/java/net/openhft/lang/MathsTest.java b/lang/src/test/java/net/openhft/lang/MathsTest.java index ab96efb..d25152b 100644 --- a/lang/src/test/java/net/openhft/lang/MathsTest.java +++ b/lang/src/test/java/net/openhft/lang/MathsTest.java @@ -1,17 +1,17 @@ /* - * Copyright 2013 Peter Lawrey + * Copyright (C) 2015 higherfrequencytrading.com * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as published by + * the Free Software Foundation, either version 3 of the License. * - * http://www.apache.org/licenses/LICENSE-2.0 + * 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 Lesser General Public License for more details. * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. + * You should have received a copy of the GNU Lesser General Public License + * along with this program. If not, see <http://www.gnu.org/licenses/>. */ package net.openhft.lang; diff --git a/lang/src/test/java/net/openhft/lang/collection/DirectBitSetTest.java b/lang/src/test/java/net/openhft/lang/collection/DirectBitSetTest.java new file mode 100644 index 0000000..438dcf9 --- /dev/null +++ b/lang/src/test/java/net/openhft/lang/collection/DirectBitSetTest.java @@ -0,0 +1,1060 @@ +/* + * Copyright (C) 2015 higherfrequencytrading.com + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as published by + * the Free Software Foundation, either version 3 of the License. + * + * 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 Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with this program. If not, see <http://www.gnu.org/licenses/>. + */ + +package net.openhft.lang.collection; + +import net.openhft.lang.io.ByteBufferBytes; +import org.junit.Test; +import org.junit.runner.RunWith; +import org.junit.runners.Parameterized; + +import java.nio.ByteBuffer; +import java.util.Arrays; +import java.util.Collection; + +import static net.openhft.lang.collection.DirectBitSet.Bits; +import static org.junit.Assert.*; + +@RunWith(value = Parameterized.class) +public class DirectBitSetTest { + + private static final int[] INDICES = new int[]{0, 50, 100, 127, 128, 255}; + private DirectBitSet bs; + private boolean singleThreaded; + private SingleThreadedDirectBitSet st; + + public DirectBitSetTest(DirectBitSet bs) { + this.bs = bs; + singleThreaded = bs instanceof SingleThreadedDirectBitSet; + if (singleThreaded) + st = (SingleThreadedDirectBitSet) bs; + assertTrue(bs.size() >= 256); + } + + @Parameterized.Parameters + public static Collection<Object[]> data() { + int capacityInBytes = 256 / 8; + return Arrays.asList(new Object[][]{ + { + ATSDirectBitSet.wrap(ByteBufferBytes.wrap( + ByteBuffer.allocate(capacityInBytes))) + }, + { + new SingleThreadedDirectBitSet(ByteBufferBytes.wrap( + ByteBuffer.allocate(capacityInBytes))) + }, + { + ATSDirectBitSet.wrap(ByteBufferBytes.wrap( + ByteBuffer.allocateDirect(capacityInBytes))) + }, + { + new SingleThreadedDirectBitSet(ByteBufferBytes.wrap( + ByteBuffer.allocateDirect(capacityInBytes))) + } + }); + } + + private void setIndices() { + bs.clear(); + for (int i : INDICES) { + bs.set(i); + } + } + + private void setIndicesComplement() { + setIndices(); + bs.flip(0, bs.size()); + } + + private void assertRangeIsClear(long from, long to) { + for (long i = from; i < to; i++) { + assertFalse(bs.get(i)); + } + } + + private void assertRangeIsClear(String message, long from, long to) { + for (long i = from; i < to; i++) { + assertFalse(message + ", bit: " + i, bs.get(i)); + } + } + + private void assertRangeIsSet(long from, long to) { + for (long i = from; i < to; i++) { + assertTrue(bs.get(i)); + } + } + + private void assertRangeIsSet(String message, long from, long to) { + for (long i = from; i < to; i++) { + assertTrue(message + ", bit: " + i, bs.get(i)); + } + } + + @Test + public void testGetSetClearAndCardinality() { + bs.clear(); + assertEquals(0, bs.cardinality()); + int c = 0; + for (int i : INDICES) { + c++; + assertFalse("At index " + i, bs.get(i)); + assertFalse("At index " + i, bs.isSet(i)); + assertTrue("At index " + i, bs.isClear(i)); + bs.set(i); + assertTrue("At index " + i, bs.get(i)); + assertTrue("At index " + i, bs.isSet(i)); + assertFalse("At index " + i, bs.isClear(i)); + assertEquals(c, bs.cardinality()); + } + + for (int i : INDICES) { + assertTrue("At index " + i, bs.get(i)); + assertTrue("At index " + i, bs.isSet(i)); + assertFalse("At index " + i, bs.isClear(i)); + bs.clear(i); + assertFalse("At index " + i, bs.get(i)); + assertFalse("At index " + i, bs.isSet(i)); + assertTrue("At index " + i, bs.isClear(i)); + } + + for (int i : INDICES) { + assertEquals("At index " + i, true, bs.setIfClear(i)); + assertEquals("At index " + i, false, bs.setIfClear(i)); + } + + for (int i : INDICES) { + assertEquals("At index " + i, true, bs.clearIfSet(i)); + assertEquals("At index " + i, false, bs.clearIfSet(i)); + } + } + + @Test + public void testGetLong() { + setIndices(); + long l0 = 1L | (1L << 50); + assertEquals(l0, bs.getLong(0)); + long l1 = (1L << (100 - 64)) | (1L << (127 - 64)); + assertEquals(l1, bs.getLong(1)); + } + + @Test + public void testFlip() { + bs.clear(); + for (int i : INDICES) { + assertEquals("At index " + i, false, bs.get(i)); + bs.flip(i); + assertEquals("At index " + i, true, bs.get(i)); + bs.flip(i); + assertEquals("At index " + i, false, bs.get(i)); + } + } + + @Test + public void testNextSetBit() { + setIndices(); + int order = 0; + for (long i = bs.nextSetBit(0L); i >= 0; i = bs.nextSetBit(i + 1)) { + assertEquals(INDICES[order], i); + order++; + } + assertEquals(-1, bs.nextSetBit(bs.size())); + + bs.clear(); + assertEquals(-1, bs.nextSetBit(0L)); + } + + @Test + public void testSetBitsIteration() { + setIndices(); + int order = 0; + Bits bits = bs.setBits(); + long i; + while ((i = bits.next()) >= 0) { + assertEquals(INDICES[order], i); + order++; + } + assertEquals(-1, bits.next()); + + bs.clear(); + assertEquals(-1, bs.setBits().next()); + } + + @Test + public void testClearNextSetBit() { + setIndices(); + long cardinality = bs.cardinality(); + int order = 0; + for (long i = bs.clearNextSetBit(0L); i >= 0; + i = bs.clearNextSetBit(i + 1)) { + assertEquals(INDICES[order], i); + assertFalse(bs.get(i)); + order++; + cardinality--; + assertEquals(cardinality, bs.cardinality()); + } + assertEquals(-1, bs.clearNextSetBit(bs.size())); + assertEquals(0, bs.cardinality()); + assertEquals(-1, bs.clearNextSetBit(0L)); + } + + @Test + public void testClearNext1SetBit() { + setIndices(); + long cardinality = bs.cardinality(); + int order = 0; + for (long i = bs.clearNextNContinuousSetBits(0L, 1); i >= 0; + i = bs.clearNextNContinuousSetBits(i + 1, 1)) { + assertEquals(INDICES[order], i); + assertFalse(bs.get(i)); + order++; + cardinality--; + assertEquals(cardinality, bs.cardinality()); + } + assertEquals(-1, bs.clearNextNContinuousSetBits(bs.size(), 1)); + assertEquals(0, bs.cardinality()); + assertEquals(-1, bs.clearNextNContinuousSetBits(0L, 1)); + } + + @Test + public void testNextSetLong() { + bs.clear(); + bs.set(0); + bs.set(255); + long[] setLongs = {0, 3}; + int order = 0; + for (long i = bs.nextSetLong(0L); i >= 0; i = bs.nextSetLong(i + 1)) { + assertEquals(setLongs[order], i); + order++; + } + assertEquals(-1, bs.nextSetLong(bs.size() / 64)); + + bs.clear(); + assertEquals(-1, bs.nextSetLong(0L)); + } + + @Test + public void testNextClearBit() { + setIndicesComplement(); + int order = 0; + for (long i = bs.nextClearBit(0L); i >= 0; i = bs.nextClearBit(i + 1)) { + assertEquals(INDICES[order], i); + order++; + } + assertEquals(-1, bs.nextClearBit(bs.size())); + + bs.setAll(); + assertEquals(-1, bs.nextClearBit(0L)); + } + + @Test + public void testSetNextClearBit() { + setIndicesComplement(); + long cardinality = bs.cardinality(); + int order = 0; + for (long i = bs.setNextClearBit(0L); i >= 0; + i = bs.setNextClearBit(i + 1)) { + assertEquals(INDICES[order], i); + assertTrue(bs.get(i)); + order++; + cardinality++; + assertEquals(cardinality, bs.cardinality()); + } + assertEquals(-1, bs.setNextClearBit(bs.size())); + assertEquals(bs.size(), bs.cardinality()); + assertEquals(-1, bs.setNextClearBit(0L)); + } + + @Test + public void testSetNext1ClearBit() { + setIndicesComplement(); + long cardinality = bs.cardinality(); + int order = 0; + for (long i = bs.setNextNContinuousClearBits(0L, 1); i >= 0; + i = bs.setNextNContinuousClearBits(i + 1, 1)) { + assertEquals(INDICES[order], i); + assertTrue(bs.get(i)); + order++; + cardinality++; + assertEquals(cardinality, bs.cardinality()); + } + assertEquals(-1, bs.setNextNContinuousClearBits(bs.size(), 1)); + assertEquals(bs.size(), bs.cardinality()); + assertEquals(-1, bs.setNextNContinuousClearBits(0L, 1)); + } + + @Test + public void testNextClearLong() { + bs.setAll(); + bs.clear(0); + bs.clear(255); + long[] clearLongs = {0, 3}; + int order = 0; + for (long i = bs.nextClearLong(0L); i >= 0; i = bs.nextClearLong(i + 1)) { + assertEquals(clearLongs[order], i); + order++; + } + assertEquals(-1, bs.nextClearLong(bs.size() / 64)); + + bs.setAll(); + assertEquals(-1, bs.nextClearLong(0L)); + } + + @Test + public void testPreviousSetBit() { + setIndices(); + int order = INDICES.length; + for (long i = bs.size(); (i = bs.previousSetBit(i - 1)) >= 0; ) { + order--; + assertEquals(INDICES[order], i); + } + assertEquals(-1, bs.previousSetBit(-1)); + + bs.clear(); + assertEquals(-1, bs.previousSetBit(bs.size())); + } + + @Test + public void testClearPreviousSetBit() { + setIndices(); + long cardinality = bs.cardinality(); + int order = INDICES.length; + for (long i = bs.size(); (i = bs.clearPreviousSetBit(i - 1)) >= 0; ) { + order--; + cardinality--; + assertEquals(INDICES[order], i); + assertFalse(bs.get(i)); + assertEquals(cardinality, bs.cardinality()); + } + assertEquals(-1, bs.clearPreviousSetBit(-1)); + assertEquals(0, bs.cardinality()); + assertEquals(-1, bs.clearPreviousSetBit(bs.size())); + } + + @Test + public void testClearPrevious1SetBit() { + setIndices(); + long cardinality = bs.cardinality(); + int order = INDICES.length; + for (long i = bs.size(); + (i = bs.clearPreviousNContinuousSetBits(i - 1, 1)) >= 0; ) { + order--; + cardinality--; + assertEquals(INDICES[order], i); + assertFalse(bs.get(i)); + assertEquals(cardinality, bs.cardinality()); + } + assertEquals(-1, bs.clearPreviousNContinuousSetBits(-1, 1)); + assertEquals(0, bs.cardinality()); + assertEquals(-1, bs.clearPreviousNContinuousSetBits(bs.size(), 1)); + } + + @Test + public void testPreviousSetLong() { + bs.clear(); + bs.set(0); + bs.set(255); + long[] setLongs = {0, 3}; + int order = setLongs.length; + for (long i = bs.size() / 64; (i = bs.previousSetLong(i - 1)) >= 0; ) { + order--; + assertEquals(setLongs[order], i); + } + assertEquals(-1, bs.previousSetLong(-1)); + + bs.clear(); + assertEquals(-1, bs.previousSetLong(bs.size() / 64)); + } + + @Test + public void testPreviousClearBit() { + setIndicesComplement(); + int order = INDICES.length; + for (long i = bs.size(); (i = bs.previousClearBit(i - 1)) >= 0; ) { + order--; + assertEquals(INDICES[order], i); + } + assertEquals(-1, bs.previousClearBit(-1)); + + bs.setAll(); + assertEquals(-1, bs.previousClearBit(bs.size())); + } + + @Test + public void testSetPreviousClearBit() { + setIndicesComplement(); + long cardinality = bs.cardinality(); + int order = INDICES.length; + for (long i = bs.size(); (i = bs.setPreviousClearBit(i - 1)) >= 0; ) { + order--; + cardinality++; + assertEquals(INDICES[order], i); + assertTrue(bs.get(i)); + assertEquals(cardinality, bs.cardinality()); + } + assertEquals(-1, bs.setPreviousClearBit(-1)); + assertEquals(bs.size(), bs.cardinality()); + assertEquals(-1, bs.setPreviousClearBit(bs.size())); + } + + @Test + public void testSetPrevious1ClearBit() { + setIndicesComplement(); + long cardinality = bs.cardinality(); + int order = INDICES.length; + for (long i = bs.size(); + (i = bs.setPreviousNContinuousClearBits(i - 1, 1)) >= 0; ) { + order--; + cardinality++; + assertEquals(INDICES[order], i); + assertTrue(bs.get(i)); + assertEquals(cardinality, bs.cardinality()); + } + assertEquals(-1, bs.setPreviousNContinuousClearBits(-1, 1)); + assertEquals(bs.size(), bs.cardinality()); + assertEquals(-1, bs.setPreviousNContinuousClearBits(bs.size(), 1)); + } + + @Test + public void testPreviousClearLong() { + bs.setAll(); + bs.clear(0); + bs.clear(255); + long[] clearLongs = {0, 3}; + int order = clearLongs.length; + for (long i = bs.size() / 64; (i = bs.previousClearLong(i - 1)) >= 0; ) { + order--; + assertEquals(clearLongs[order], i); + } + assertEquals(-1, bs.previousClearLong(-1)); + + bs.setAll(); + assertEquals(-1, bs.previousClearLong(bs.size() / 64)); + } + + @Test + public void testSetAll() { + bs.clear(); + bs.setAll(); + assertEquals(bs.size(), bs.cardinality()); + } + + @Test + public void testRangeOpsWithinLongCase() { + bs.clear(); + if (singleThreaded) { + assertTrue(st.allClear(0, 0)); + assertTrue(st.allClear(63, 63)); + assertTrue(st.allSet(0, 0)); + assertTrue(st.allSet(63, 63)); + } + + bs.flip(0, 0); + assertEquals(false, bs.get(0)); + assertEquals(0, bs.cardinality()); + bs.flip(0, 1); + assertEquals(true, bs.get(0)); + assertEquals(1, bs.cardinality()); + if (singleThreaded) { + assertTrue(st.allSet(0, 1)); + assertFalse(st.allSet(0, 2)); + assertFalse(st.allClear(0, 1)); + } + + bs.clear(0, 0); + assertEquals(true, bs.get(0)); + assertEquals(1, bs.cardinality()); + bs.clear(0, 1); + assertEquals(false, bs.get(0)); + assertEquals(0, bs.cardinality()); + + bs.set(0, 0); + assertEquals(false, bs.get(0)); + assertEquals(0, bs.cardinality()); + bs.set(0, 1); + assertEquals(true, bs.get(0)); + assertEquals(1, bs.cardinality()); + } + + @Test + public void testRangeOpsCrossLongCase() { + bs.clear(); + + bs.flip(63, 64); + assertEquals(true, bs.get(63)); + assertEquals(false, bs.get(64)); + assertEquals(1, bs.cardinality()); + if (singleThreaded) { + assertFalse(st.allSet(63, 65)); + assertFalse(st.allClear(63, 65)); + } + bs.flip(63, 65); + assertEquals(false, bs.get(63)); + assertEquals(true, bs.get(64)); + assertEquals(1, bs.cardinality()); + if (singleThreaded) { + assertFalse(st.allSet(63, 65)); + assertFalse(st.allClear(63, 65)); + } + + bs.clear(64); + bs.set(63, 64); + assertEquals(true, bs.get(63)); + assertEquals(false, bs.get(64)); + assertEquals(1, bs.cardinality()); + + bs.set(64); + bs.clear(63, 64); + assertEquals(false, bs.get(63)); + assertEquals(true, bs.get(64)); + assertEquals(1, bs.cardinality()); + + bs.clear(64); + bs.set(63, 65); + assertEquals(true, bs.get(63)); + assertEquals(true, bs.get(64)); + assertEquals(2, bs.cardinality()); + if (singleThreaded) { + assertTrue(st.allSet(63, 65)); + assertFalse(st.allClear(63, 65)); + } + + bs.clear(63, 65); + assertEquals(false, bs.get(63)); + assertEquals(false, bs.get(64)); + assertEquals(0, bs.cardinality()); + if (singleThreaded) { + assertFalse(st.allSet(63, 65)); + assertTrue(st.allClear(63, 65)); + } + } + + @Test + public void testRangeOpsSpanLongCase() { + bs.clear(); + if (singleThreaded) { + assertTrue(st.allClear(0, st.size())); + assertFalse(st.allSet(0, st.size())); + } + + bs.set(0, bs.size()); + assertEquals(bs.size(), bs.cardinality()); + if (singleThreaded) { + assertFalse(st.allClear(0, st.size())); + assertTrue(st.allSet(0, st.size())); + } + + bs.clear(0, bs.size()); + assertEquals(0, bs.cardinality()); + + bs.flip(0, bs.size()); + assertEquals(bs.size(), bs.cardinality()); + } + + private String m(int n) { + return "N: " + n + ", " + bs.getClass().getSimpleName(); + } + + @Test + public void testSetNextNContinuousClearBitsWithinLongCase() { + long size = (bs.size() + 63) / 64 * 64; + for (int n = 1; n <= 64; n *= 2) { + bs.clear(); + for (int i = 0; i < size / n; i++) { + assertRangeIsClear(i * n, i * n + n); + assertEquals(m(n), i * n, bs.setNextNContinuousClearBits(0L, n)); + assertRangeIsSet(i * n, i * n + n); + assertEquals(i * n + n, bs.cardinality()); + } + } + for (int n = 2; n <= 64; n *= 2) { + bs.setAll(); + bs.clear(size - n, size); + assertEquals(size - n, bs.setNextNContinuousClearBits(0L, n)); + assertRangeIsSet(size - n, size); + + long offset = (64 - n) / 2; + long from = size - n - offset; + long to = size - offset; + bs.clear(from, to); + assertEquals(from, bs.setNextNContinuousClearBits(from, n)); + assertRangeIsSet(from, to); + + bs.clear(from, to); + for (long i = from - 2; i >= 0; i -= 2) { + bs.clear(i); + } + long cardinality = bs.cardinality(); + assertEquals(from, bs.setNextNContinuousClearBits(0, n)); + assertEquals(cardinality + n, bs.cardinality()); + } + } + + @Test + public void testSetNextNContinuousClearBitsCrossLongCase() { + if (bs instanceof ATSDirectBitSet) + return; + long size = bs.size(); + for (int n : new int[]{3, 7, 13, 31, 33, 63, 65, 100, 127, 128, 129, 254, 255}) { + bs.clear(); + for (int i = 0; i < size / n; i++) { + assertRangeIsClear(i * n, i * n + n); + assertEquals(m(n), i * n, bs.setNextNContinuousClearBits(0L, n)); + assertRangeIsSet(i * n, i * n + n); + assertEquals(i * n + n, bs.cardinality()); + } + } + long lastBound = size - (size % 64 == 0 ? 64 : size % 64); + for (int n : new int[]{2, 3, 7, 13, 31, 33, 63, 64, 65, 100, 127, 128, 129}) { + bs.setAll(); + long from = n <= 64 ? lastBound - (n / 2) : 30; + long to = from + n; + bs.clear(from, to); + assertEquals("" + n, from, bs.setNextNContinuousClearBits(0L, n)); + assertRangeIsSet(from, to); + + bs.clear(from, to); + for (long i = from - 2; i >= 0; i -= 2) { + bs.clear(i); + } + for (long i = to + 1; i < bs.size(); i += 2) { + bs.clear(i); + } + long cardinality = bs.cardinality(); + assertEquals(from, bs.setNextNContinuousClearBits(from, n)); + assertEquals(cardinality + n, bs.cardinality()); + } + } + + @Test + public void testClearNextNContinuousSetBitsWithinLongCase() { + long size = (bs.size() + 63) / 64 * 64; + for (int n = 1; n <= 64; n *= 2) { + bs.setAll(); + long cardinality = bs.cardinality(); + for (int i = 0; i < size / n; i++) { + assertRangeIsSet(i * n, i * n + n); + assertEquals(m(n), i * n, bs.clearNextNContinuousSetBits(0L, n)); + assertRangeIsClear(i * n, i * n + n); + assertEquals(cardinality - (i * n + n), bs.cardinality()); + } + } + for (int n = 2; n <= 64; n *= 2) { + bs.clear(); + bs.set(size - n, size); + assertEquals(size - n, bs.clearNextNContinuousSetBits(0L, n)); + assertRangeIsClear(size - n, size); + + long offset = (64 - n) / 2; + long from = size - n - offset; + long to = size - offset; + bs.set(from, to); + assertEquals(from, bs.clearNextNContinuousSetBits(from, n)); + assertRangeIsClear(from, to); + + bs.set(from, to); + for (long i = from - 2; i >= 0; i -= 2) { + bs.set(i); + } + long cardinality = bs.cardinality(); + assertEquals(from, bs.clearNextNContinuousSetBits(0, n)); + assertEquals(cardinality - n, bs.cardinality()); + } + } + + @Test + public void testClearNextNContinuousSetBitsCrossLongCase() { + if (bs instanceof ATSDirectBitSet) + return; + long size = bs.size(); + for (int n : new int[]{3, 7, 13, 31, 33, 63}) { + bs.setAll(); + long cardinality = bs.cardinality(); + for (int i = 0; i < size / n; i++) { + assertRangeIsSet(i * n, i * n + n); + assertEquals(m(n), i * n, bs.clearNextNContinuousSetBits(0L, n)); + assertRangeIsClear(i * n, i * n + n); + assertEquals(cardinality -= n, bs.cardinality()); + } + } + long lastBound = size - (size % 64 == 0 ? 64 : size % 64); + for (int n : new int[]{2, 3, 7, 13, 31, 33, 63, 64}) { + bs.clear(); + long from = lastBound - (n / 2); + long to = from + n; + bs.set(from, to); + assertEquals(from, bs.clearNextNContinuousSetBits(0L, n)); + assertRangeIsClear(from, to); + + bs.set(from, to); + for (long i = from - 2; i >= 0; i -= 2) { + bs.set(i); + } + for (long i = to + 1; i < bs.size(); i += 2) { + bs.set(i); + } + long cardinality = bs.cardinality(); + assertEquals(from, bs.clearNextNContinuousSetBits(from, n)); + assertEquals(cardinality - n, bs.cardinality()); + } + } + + @Test + public void testSetPreviousNContinuousClearBitsWithinLongCase() { + long size = (bs.size() + 63) / 64 * 64; + for (int n = 1; n <= 64; n *= 2) { + bs.clear(); + long cardinality = 0; + for (long i = size / n - 1; i >= 0; i--) { + assertRangeIsClear(i * n, i * n + n); + assertEquals(m(n), i * n, bs.setPreviousNContinuousClearBits(size, n)); + assertRangeIsSet(i * n, i * n + n); + assertEquals(cardinality += n, bs.cardinality()); + } + } + for (int n = 2; n <= 64; n *= 2) { + bs.setAll(); + bs.clear(0, n); + assertEquals(0, bs.setPreviousNContinuousClearBits(bs.size(), n)); + assertRangeIsSet(0, n); + + long from = (64 - n) / 2; + long to = from + n; + bs.clear(from, to); + assertEquals(from, bs.setPreviousNContinuousClearBits(to - 1, n)); + assertRangeIsSet(from, to); + + bs.clear(from, to); + for (long i = to + 1; i < bs.size(); i += 2) { + bs.clear(i); + } + long cardinality = bs.cardinality(); + assertEquals(from, bs.setPreviousNContinuousClearBits(bs.size(), n)); + assertEquals(cardinality + n, bs.cardinality()); + } + } + + @Test + public void testSetPreviousNContinuousClearBitsCrossLongCase() { + if (bs instanceof ATSDirectBitSet) + return; + long size = bs.size(); + for (int n : new int[]{3, 7, 13, 31, 33, 63}) { + bs.clear(); + long cardinality = 0; + for (long from = size - n; from >= 0; from -= n) { + assertRangeIsClear(from, from + n); + assertEquals(m(n), from, bs.setPreviousNContinuousClearBits(size, n)); + assertRangeIsSet(from, from + n); + assertEquals(cardinality += n, bs.cardinality()); + } + } + for (int n : new int[]{2, 3, 7, 13, 31, 33, 63, 64}) { + bs.setAll(); + long from = 64 - (n / 2); + long to = from + n; + bs.clear(from, to); + assertEquals(from, bs.setPreviousNContinuousClearBits(size, n)); + assertRangeIsSet(from, to); + + bs.clear(from, to); + for (long i = from - 2; i >= 0; i -= 2) { + bs.clear(i); + } + for (long i = to + 1; i < bs.size(); i += 2) { + bs.clear(i); + } + long cardinality = bs.cardinality(); + assertEquals(from, bs.setPreviousNContinuousClearBits(to - 1, n)); + assertEquals(cardinality + n, bs.cardinality()); + } + } + + @Test + public void testClearPreviousNContinuousSetBitsWithinLongCase() { + long size = (bs.size() + 63) / 64 * 64; + for (int n = 1; n <= 64; n *= 2) { + bs.setAll(); + long cardinality = bs.cardinality(); + for (long i = size / n - 1; i >= 0; i--) { + assertRangeIsSet(i * n, i * n + n); + assertEquals(m(n), i * n, bs.clearPreviousNContinuousSetBits(size, n)); + assertRangeIsClear(m(n), i * n, i * n + n); + assertEquals(cardinality -= n, bs.cardinality()); + } + } + for (int n = 2; n <= 64; n *= 2) { + bs.clear(); + bs.set(0, n); + assertEquals(0, bs.clearPreviousNContinuousSetBits(bs.size(), n)); + assertRangeIsClear(0, n); + + long from = (64 - n) / 2; + long to = from + n; + bs.set(from, to); + assertEquals(from, bs.clearPreviousNContinuousSetBits(to - 1, n)); + assertRangeIsClear(from, to); + + bs.set(from, to); + for (long i = to + 1; i < bs.size(); i += 2) { + bs.set(i); + } + long cardinality = bs.cardinality(); + assertEquals(from, bs.clearPreviousNContinuousSetBits(bs.size(), n)); + assertEquals(cardinality - n, bs.cardinality()); + } + } + + @Test + public void testClearPreviousNContinuousSetBitsCrossLongCase() { + if (bs instanceof ATSDirectBitSet) + return; + long size = bs.size(); + for (int n : new int[]{3, 7, 13, 31, 33, 63}) { + bs.setAll(); + long cardinality = bs.cardinality(); + for (long from = size - n; from >= 0; from -= n) { + assertRangeIsSet(from, from + n); + assertEquals(m(n), from, bs.clearPreviousNContinuousSetBits(size, n)); + assertRangeIsClear(from, from + n); + assertEquals(cardinality -= n, bs.cardinality()); + } + } + for (int n : new int[]{2, 3, 7, 13, 31, 33, 63, 64}) { + bs.clear(); + long from = 64 - (n / 2); + long to = from + n; + bs.set(from, to); + assertEquals(from, bs.clearPreviousNContinuousSetBits(size, n)); + assertRangeIsClear(from, to); + + bs.set(from, to); + for (long i = from - 2; i >= 0; i -= 2) { + bs.set(i); + } + for (long i = to + 1; i < bs.size(); i += 2) { + bs.set(i); + } + long cardinality = bs.cardinality(); + assertEquals(from, bs.clearPreviousNContinuousSetBits(to - 1, n)); + assertEquals(cardinality - n, bs.cardinality()); + } + } + + @Test(expected = IndexOutOfBoundsException.class) + public void testIoobeGetNegative() { + bs.get(-1); + } + + @Test(expected = IndexOutOfBoundsException.class) + public void testIoobeGetOverCapacity() { + bs.get(bs.size()); + } + + @Test(expected = IndexOutOfBoundsException.class) + public void testIoobeSetNegative() { + bs.set(-1); + } + + @Test(expected = IndexOutOfBoundsException.class) + public void testIoobeSetOverCapacity() { + bs.set(bs.size()); + } + + @Test(expected = IndexOutOfBoundsException.class) + public void testIoobeSetIfClearNegative() { + bs.setIfClear(-1); + } + + @Test(expected = IndexOutOfBoundsException.class) + public void testIoobeSetIfClearOverCapacity() { + bs.setIfClear(bs.size()); + } + + @Test(expected = IndexOutOfBoundsException.class) + public void testIoobeClearIfSetNegative() { + bs.clearIfSet(-1); + } + + @Test(expected = IndexOutOfBoundsException.class) + public void testIoobeClearIfSetOverCapacity() { + bs.clearIfSet(bs.size()); + } + + @Test(expected = IndexOutOfBoundsException.class) + public void testIoobeFlipNegative() { + bs.flip(-1); + } + + @Test(expected = IndexOutOfBoundsException.class) + public void testIoobeFlipOverCapacity() { + bs.flip(bs.size()); + } + + @Test(expected = IndexOutOfBoundsException.class) + public void testIoobeGetLongNegative() { + bs.getLong(-1); + } + + @Test(expected = IndexOutOfBoundsException.class) + public void testIoobeGetLongOverCapacity() { + bs.getLong((bs.size() + 63) / 64); + } + + @Test(expected = IndexOutOfBoundsException.class) + public void testIoobeNextSetBit() { + bs.nextSetBit(-1); + } + + @Test(expected = IndexOutOfBoundsException.class) + public void testIoobeNextSetLong() { + bs.nextSetLong(-1); + } + + @Test(expected = IndexOutOfBoundsException.class) + public void testIoobeNextClearBit() { + bs.nextClearBit(-1); + } + + @Test(expected = IndexOutOfBoundsException.class) + public void testIoobeNextClearLong() { + bs.nextClearLong(-1); + } + + @Test(expected = IndexOutOfBoundsException.class) + public void testIoobePreviousSetBit() { + bs.previousSetBit(-2); + } + + @Test(expected = IndexOutOfBoundsException.class) + public void testIoobePreviousSetLong() { + bs.previousSetLong(-2); + } + + @Test(expected = IndexOutOfBoundsException.class) + public void testIoobePreviousClearBit() { + bs.previousClearBit(-2); + } + + @Test(expected = IndexOutOfBoundsException.class) + public void testIoobePreviousClearLong() { + bs.previousClearLong(-2); + } + + @Test(expected = IndexOutOfBoundsException.class) + public void testIoobeClearNextSetBit() { + bs.clearNextSetBit(-1); + } + + @Test(expected = IndexOutOfBoundsException.class) + public void testIoobeClearNextNContinuousSetBits() { + bs.clearNextNContinuousSetBits(-1, 2); + } + + @Test(expected = IndexOutOfBoundsException.class) + public void testIoobeSetNextClearBit() { + bs.setNextClearBit(-1); + } + + @Test(expected = IndexOutOfBoundsException.class) + public void testIoobeSetNextNContinuousClearBits() { + bs.setNextNContinuousClearBits(-1, 2); + } + + @Test(expected = IndexOutOfBoundsException.class) + public void testIoobeClearPreviousSetBit() { + bs.clearPreviousSetBit(-2); + } + + @Test(expected = IndexOutOfBoundsException.class) + public void testIoobeClearPreviousNContinuousSetBit() { + bs.clearPreviousNContinuousSetBits(-2, 2); + } + + @Test(expected = IndexOutOfBoundsException.class) + public void testIoobeSetPreviousClearBit() { + bs.setPreviousClearBit(-2); + } + + @Test(expected = IndexOutOfBoundsException.class) + public void testIoobeSetPreviousNContinuousClearBit() { + bs.setPreviousNContinuousClearBits(-2, 2); + } + + @Test(expected = IndexOutOfBoundsException.class) + public void testIoobeSetRangeFromNegative() { + bs.set(-1, 0); + } + + @Test(expected = IndexOutOfBoundsException.class) + public void testIoobeSetRangeFromOverTo() { + bs.set(1, 0); + } + + @Test(expected = IndexOutOfBoundsException.class) + public void testIoobeSetRangeToOverCapacity() { + bs.set(0, bs.size() + 1); + } + + @Test(expected = IndexOutOfBoundsException.class) + public void testIoobeClearRangeFromNegative() { + bs.clear(-1, 0); + } + + @Test(expected = IndexOutOfBoundsException.class) + public void testIoobeClearRangeFromOverTo() { + bs.clear(1, 0); + } + + @Test(expected = IndexOutOfBoundsException.class) + public void testIoobeClearRangeToOverCapacity() { + bs.clear(0, bs.size() + 1); + } + + @Test(expected = IndexOutOfBoundsException.class) + public void testIoobeFlipRangeFromNegative() { + bs.flip(-1, 0); + } + + @Test(expected = IndexOutOfBoundsException.class) + public void testIoobeFlipRangeFromOverTo() { + bs.flip(1, 0); + } + + @Test(expected = IndexOutOfBoundsException.class) + public void testIoobeFlipRangeToOverCapacity() { + bs.flip(0, bs.size() + 1); + } + + @Test(expected = IllegalArgumentException.class) + public void testIaeClearNextNContinuousSetBits() { + bs.clearNextNContinuousSetBits(0, 0); + } + + @Test(expected = IllegalArgumentException.class) + public void testIaeSetNextNContinuousClearBits() { + bs.setNextNContinuousClearBits(0, 0); + } + + @Test(expected = IllegalArgumentException.class) + public void testIaeClearPreviousNContinuousSetBits() { + bs.clearPreviousNContinuousSetBits(bs.size(), 0); + } + + @Test(expected = IllegalArgumentException.class) + public void testIaeSetPreviousNContinuousClearBits() { + bs.setPreviousNContinuousClearBits(bs.size(), 0); + } +} diff --git a/lang/src/test/java/net/openhft/lang/collection/HugeArrayTest.java b/lang/src/test/java/net/openhft/lang/collection/HugeArrayTest.java index 0b10e01..ce227dc 100644 --- a/lang/src/test/java/net/openhft/lang/collection/HugeArrayTest.java +++ b/lang/src/test/java/net/openhft/lang/collection/HugeArrayTest.java @@ -1,17 +1,17 @@ /* - * Copyright 2013 Peter Lawrey + * Copyright (C) 2015 higherfrequencytrading.com * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as published by + * the Free Software Foundation, either version 3 of the License. * - * http://www.apache.org/licenses/LICENSE-2.0 + * 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 Lesser General Public License for more details. * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. + * You should have received a copy of the GNU Lesser General Public License + * along with this program. If not, see <http://www.gnu.org/licenses/>. */ package net.openhft.lang.collection; @@ -32,52 +32,84 @@ public class HugeArrayTest { org.junit.Assert.assertEquals(a, b); } + /* + * Test proves that you can get more than one object from the array + * The first object is created at construction time to get the size of an element + * The second one is created on the fly due to freelist being empty. + * There was a bug where acquire() was creating a heap object on freelist miss + */ + @Test + public void testGetTwoObjects() { + HugeArray<JavaBeanInterface> array = + HugeCollections.newArray(JavaBeanInterface.class, 2); + JavaBeanInterface obj1 = array.get(0); + JavaBeanInterface obj2 = array.get(1); +/* +Can't call recycle due to recycle using .equals checks on your model. +Two unmodified objects are .equals(). +Recycle should use identity to know if it's putting the same object back in the list. + + array.recycle(obj1); + array.recycle(obj2); +*/ + } + + /* + With lock: false, average time to access a JavaBeanInterface was 71.9 ns + With lock: true, average time to access a JavaBeanInterface was 124.7 ns + */ @Test public void testHugeArray() throws InterruptedException { // runs with a maximum heap size of 32 MB. int length = 10 * 1000 * 1000; HugeArray<JavaBeanInterface> array = HugeCollections.newArray(JavaBeanInterface.class, length); - long start = System.nanoTime(); - for (int i = 0; i < array.length(); i++) { - JavaBeanInterface jbi = array.get(i); -// jbi.busyLockRecord(); -// try { - jbi.setByte((byte) i); - jbi.setChar((char) i); - jbi.setShort((short) i); - jbi.setInt(i); - jbi.setFloat(i); - jbi.setLong(i); // System.nanoTime()); - jbi.setDouble(i); - jbi.setFlag((i & 3) == 0); - jbi.setString("hello"); -// } finally { -// jbi.unlockRecord(); -// } - array.recycle(jbi); - } - for (int i = 0; i < array.length(); i++) { - JavaBeanInterface jbi = array.get(i); -// jbi.busyLockRecord(); -// try { - assertEquals2((byte) i, jbi.getByte()); - assertEquals2((char) i, jbi.getChar()); - assertEquals2((short) i, jbi.getShort()); - assertEquals2(i, jbi.getInt()); - assertEquals(i, jbi.getFloat(), 0); + for (boolean withLock : new boolean[]{false, true}) { + long start = System.nanoTime(); + for (int i = 0; i < array.length(); i++) { + JavaBeanInterface jbi = array.get(i); + if (withLock) + jbi.busyLockRecord(); + try { + jbi.setByte((byte) i); + jbi.setChar((char) i); + jbi.setShort((short) i); + jbi.setInt(i); + jbi.setFloat(i); + jbi.setLong(i); // System.nanoTime()); + jbi.setDouble(i); + jbi.setFlag((i & 3) == 0); + jbi.setString("hello"); + } finally { + if (withLock) + jbi.unlockRecord(); + } + array.recycle(jbi); + } + for (int i = 0; i < array.length(); i++) { + JavaBeanInterface jbi = array.get(i); + if (withLock) + jbi.busyLockRecord(); + try { + assertEquals2((byte) i, jbi.getByte()); + assertEquals2((char) i, jbi.getChar()); + assertEquals2((short) i, jbi.getShort()); + assertEquals2(i, jbi.getInt()); + assertEquals(i, jbi.getFloat(), 0); // long time = System.nanoTime() - jbi.getLong(); - assertEquals2(i, jbi.getLong()); - assertEquals(i, jbi.getDouble(), 0.0); - assertEquals((i & 3) == 0, jbi.getFlag()); - assertEquals("hello", jbi.getString()); -// } finally { -// jbi.unlockRecord(); -// } - array.recycle(jbi); + assertEquals2(i, jbi.getLong()); + assertEquals(i, jbi.getDouble(), 0.0); + assertEquals((i & 3) == 0, jbi.getFlag()); + assertEquals("hello", jbi.getString()); + } finally { + if (withLock) + jbi.unlockRecord(); + } + array.recycle(jbi); + } + long time = System.nanoTime() - start; + double avg = time / 2.0 / length; + System.out.printf("With lock: %s, average time to access a JavaBeanInterface was %.1f ns%n", withLock, avg); } - long time = System.nanoTime() - start; - double avg = time / 2.0 / length; - System.out.printf("Average time to access a JavaBeanInterface was %.1f ns%n", avg); } } diff --git a/lang/src/test/java/net/openhft/lang/collection/HugePricesMain.java b/lang/src/test/java/net/openhft/lang/collection/HugePricesMain.java index 2613063..af3977f 100644 --- a/lang/src/test/java/net/openhft/lang/collection/HugePricesMain.java +++ b/lang/src/test/java/net/openhft/lang/collection/HugePricesMain.java @@ -1,23 +1,24 @@ /* - * Copyright 2013 Peter Lawrey + * Copyright (C) 2015 higherfrequencytrading.com * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as published by + * the Free Software Foundation, either version 3 of the License. * - * http://www.apache.org/licenses/LICENSE-2.0 + * 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 Lesser General Public License for more details. * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. + * You should have received a copy of the GNU Lesser General Public License + * along with this program. If not, see <http://www.gnu.org/licenses/>. */ package net.openhft.lang.collection; import net.openhft.lang.model.constraints.MaxSize; import net.openhft.lang.model.constraints.Range; +import org.junit.Test; import static org.junit.Assert.assertEquals; @@ -63,7 +64,9 @@ interface TimedQuote extends Quote { } public class HugePricesMain { - public static void main(String[] args) { + + @Test + public void test() { int length = 1000; final HugeArray<Price> prices = HugeCollections.newArray(Price.class, length); @@ -72,7 +75,7 @@ public class HugePricesMain { price.setInstrument("ID" + i); price.getAsk().setPrice(100.1); price.getAsk().setAmount(1000); - price.getBid().setPrice(99.8); + price.getBid().setPrice(99.8123456789); price.getBid().setAmount(2000); prices.recycle(price); } @@ -81,7 +84,7 @@ public class HugePricesMain { assertEquals("ID" + i, price.getInstrument()); assertEquals(100.1, price.getAsk().getPrice(), 0.0); assertEquals(1000, price.getAsk().getAmount()); - assertEquals(99.8, price.getBid().getPrice(), 0.0); + assertEquals(99.8123456789, price.getBid().getPrice(), 0.0); assertEquals(2000, price.getBid().getAmount()); prices.recycle(price); } diff --git a/lang/src/test/java/net/openhft/lang/collection/HugeQueueTest.java b/lang/src/test/java/net/openhft/lang/collection/HugeQueueTest.java index 5c5d01c..b68f6b1 100644 --- a/lang/src/test/java/net/openhft/lang/collection/HugeQueueTest.java +++ b/lang/src/test/java/net/openhft/lang/collection/HugeQueueTest.java @@ -1,17 +1,17 @@ /* - * Copyright 2013 Peter Lawrey + * Copyright (C) 2015 higherfrequencytrading.com * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as published by + * the Free Software Foundation, either version 3 of the License. * - * http://www.apache.org/licenses/LICENSE-2.0 + * 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 Lesser General Public License for more details. * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. + * You should have received a copy of the GNU Lesser General Public License + * along with this program. If not, see <http://www.gnu.org/licenses/>. */ package net.openhft.lang.collection; @@ -66,6 +66,5 @@ public class HugeQueueTest { long time = System.nanoTime() - start; double avg = time / 2.0 / tests; System.out.printf("Average time to access a JavaBeanInterface was %.1f ns%n", avg); - } } diff --git a/lang/src/test/java/net/openhft/lang/example/CounterExampleMain.java b/lang/src/test/java/net/openhft/lang/example/CounterExampleMain.java new file mode 100644 index 0000000..41135be --- /dev/null +++ b/lang/src/test/java/net/openhft/lang/example/CounterExampleMain.java @@ -0,0 +1,48 @@ +/* + * Copyright (C) 2015 higherfrequencytrading.com + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as published by + * the Free Software Foundation, either version 3 of the License. + * + * 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 Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with this program. If not, see <http://www.gnu.org/licenses/>. + */ + +package net.openhft.lang.example; + +import net.openhft.lang.io.DirectBytes; +import net.openhft.lang.io.MappedStore; + +import java.io.File; +import java.io.IOException; +import java.nio.channels.FileChannel; + +public class CounterExampleMain { + static volatile long id; + + public static void main(String... ignored) throws IOException { + int counters = 128; + int repeats = 100000; + + File file = new File(System.getProperty("java.io.tmpdir") + "/counters"); + MappedStore ms = new MappedStore(file, FileChannel.MapMode.READ_WRITE, counters * 8); + DirectBytes slice = ms.bytes(); + + long start = System.nanoTime(); + for (int j = 0; j < repeats; j++) { + for (int i = 0; i < counters; i++) { + id = slice.addAtomicLong(i * 8, 1); + } + } + long time = System.nanoTime() - start; + System.out.printf("Took %.3f second to increment %,d counters, %,d times, last id=%,d%n", + time / 1e9, counters, repeats, id); + ms.free(); + } +} diff --git a/lang/src/test/java/net/openhft/lang/io/AllocationRatesTest.java b/lang/src/test/java/net/openhft/lang/io/AllocationRatesTest.java index da8696a..66cde7b 100644..100755 --- a/lang/src/test/java/net/openhft/lang/io/AllocationRatesTest.java +++ b/lang/src/test/java/net/openhft/lang/io/AllocationRatesTest.java @@ -1,17 +1,17 @@ /* - * Copyright 2013 Peter Lawrey + * Copyright (C) 2015 higherfrequencytrading.com * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as published by + * the Free Software Foundation, either version 3 of the License. * - * http://www.apache.org/licenses/LICENSE-2.0 + * 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 Lesser General Public License for more details. * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. + * You should have received a copy of the GNU Lesser General Public License + * along with this program. If not, see <http://www.gnu.org/licenses/>. */ package net.openhft.lang.io; @@ -21,7 +21,7 @@ import org.junit.Test; import java.nio.ByteBuffer; /** - * User: peter Date: 24/12/13 Time: 19:43 + * User: peter.lawrey Date: 24/12/13 Time: 19:43 */ /* buffers 128 KB took an average of 18,441 ns for heap ByteBuffer, 33,683 ns for direct ByteBuffer and 1,761 for DirectStore @@ -32,7 +32,7 @@ buffers 128 KB took an average of 8,739 ns for heap ByteBuffer, 22,684 ns for di */ public class AllocationRatesTest { static final int BUFFER_SIZE = 128 * 1024; - static final int ALLOCATIONS = 25000; + static final int ALLOCATIONS = 10000; public static final int BATCH = 10; @Test diff --git a/lang/src/test/java/net/openhft/lang/io/BigDecimalVsDoubleMain.java b/lang/src/test/java/net/openhft/lang/io/BigDecimalVsDoubleMain.java new file mode 100644 index 0000000..609bc03 --- /dev/null +++ b/lang/src/test/java/net/openhft/lang/io/BigDecimalVsDoubleMain.java @@ -0,0 +1,110 @@ +/* + * Copyright (C) 2015 higherfrequencytrading.com + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as published by + * the Free Software Foundation, either version 3 of the License. + * + * 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 Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with this program. If not, see <http://www.gnu.org/licenses/>. + */ + +package net.openhft.lang.io; + +import java.math.BigDecimal; +import java.nio.ByteBuffer; + +public class BigDecimalVsDoubleMain { + + public static final String[] NUMBER = {"1000000", "1.1", "1.23456", "12345.67890"}; + public static final Bytes[] IN_BYTES = new Bytes[NUMBER.length]; + public static final Bytes OUT_BYTES; + + static { + DirectStore store = new DirectStore((NUMBER.length + 1) * 16); + for (int i = 0; i < NUMBER.length; i++) { + IN_BYTES[i] = store.bytes((i + 1) * 16, 16); + IN_BYTES[i].append(NUMBER[i]); + } + OUT_BYTES = store.bytes(0, 16); + } + + static int count = 0; + + public static void main(String[] args) throws InterruptedException { + Bytes x = ByteBufferBytes.wrap(ByteBuffer.allocateDirect(16)); + x.writeUTFΔ("Hello World"); + System.out.println(x); + int runs = 5000; + + for (int t = 0; t < 5; t++) { + long timeD = 0; + long timeBD = 0; + long timeB = 0; + if (t == 0) + System.out.println("Warming up"); + else if (t == 1) + System.out.println("Cold code"); + + int r = t == 0 ? 20000 : runs; + for (int i = 0; i < r; i += 3) { + count++; + if (count >= NUMBER.length) count = 0; + + if (t > 0) + Thread.sleep(1); + timeB += testDoubleWithBytes(); + timeBD += testBigDecimalWithString(); + timeD += testDoubleWithString(); + if (t > 0) + Thread.sleep(1); + timeD += testDoubleWithString(); + timeB += testDoubleWithBytes(); + timeBD += testBigDecimalWithString(); + if (t > 0) + Thread.sleep(1); + timeBD += testBigDecimalWithString(); + timeD += testDoubleWithString(); + timeB += testDoubleWithBytes(); + } + System.out.printf("double took %.1f us, BigDecimal took %.1f, double with Bytes took %.1f%n", + timeD / 1e3 / r, timeBD / 1e3 / r, timeB / 1e3 / r); + } + } + + static volatile double saved; + static volatile String savedStr; + + public static long testDoubleWithString() { + long start = System.nanoTime(); + saved = Double.parseDouble(NUMBER[count]); + savedStr = Double.toString(saved); + return System.nanoTime() - start; + } + + public static long testDoubleWithBytes() { + IN_BYTES[count].position(0); + OUT_BYTES.position(0); + + long start = System.nanoTime(); + saved = IN_BYTES[count].parseDouble(); + OUT_BYTES.append(saved); + System.out.println(OUT_BYTES); + + return System.nanoTime() - start; + } + + static volatile BigDecimal savedBD; + + public static long testBigDecimalWithString() { + long start = System.nanoTime(); + savedBD = new BigDecimal(NUMBER[count]); + savedStr = savedBD.toString(); + return System.nanoTime() - start; + } +} diff --git a/lang/src/test/java/net/openhft/lang/io/ByteBufferBytesTest.java b/lang/src/test/java/net/openhft/lang/io/ByteBufferBytesTest.java index 9c2ec03..8ba8b65 100644..100755 --- a/lang/src/test/java/net/openhft/lang/io/ByteBufferBytesTest.java +++ b/lang/src/test/java/net/openhft/lang/io/ByteBufferBytesTest.java @@ -1,28 +1,32 @@ /* - * Copyright 2013 Peter Lawrey + * Copyright (C) 2015 higherfrequencytrading.com * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as published by + * the Free Software Foundation, either version 3 of the License. * - * http://www.apache.org/licenses/LICENSE-2.0 + * 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 Lesser General Public License for more details. * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. + * You should have received a copy of the GNU Lesser General Public License + * along with this program. If not, see <http://www.gnu.org/licenses/>. */ package net.openhft.lang.io; import net.openhft.lang.Maths; +import net.openhft.lang.io.serialization.BytesMarshallable; +import net.openhft.lang.model.DataValueClasses; +import net.openhft.lang.model.constraints.MaxSize; import net.openhft.lang.thread.NamedThreadFactory; import org.junit.Before; import org.junit.Test; import java.io.*; import java.math.BigDecimal; +import java.nio.BufferUnderflowException; import java.nio.ByteBuffer; import java.nio.ByteOrder; import java.text.SimpleDateFormat; @@ -31,27 +35,25 @@ import java.util.concurrent.*; import java.util.zip.GZIPInputStream; import java.util.zip.GZIPOutputStream; -import static net.openhft.lang.io.StopCharTesters.CONTROL_STOP; -import static net.openhft.lang.io.StopCharTesters.SPACE_STOP; +import static net.openhft.lang.io.StopCharTesters.*; import static org.junit.Assert.*; /** - * Created with IntelliJ IDEA. User: peter.lawrey Date: 17/09/13 Time: 16:09 To change this template use File | Settings | File - * Templates. + * User: peter.lawrey */ public class ByteBufferBytesTest { public static final int SIZE = 128; - private ByteBufferBytes bytes; + private Bytes bytes; private ByteBuffer byteBuffer; @Before public void beforeTest() { byteBuffer = ByteBuffer.allocate(SIZE).order(ByteOrder.nativeOrder()); - bytes = new ByteBufferBytes(byteBuffer); + bytes = ByteBufferBytes.wrap(byteBuffer); } @Test - public void testLongHash() throws Exception { + public void testLongHash() { byte[] bytes = {1, 2, 3, 4, 5, 6, 7, 8}; long h = NativeBytes.longHash(bytes, 0, bytes.length); assertFalse(h == 0); @@ -62,7 +64,14 @@ public class ByteBufferBytesTest { } @Test - public void testRead() throws Exception { + public void testCAS() { + Bytes bytes = ByteBufferBytes.wrap(ByteBuffer.allocate(100)); + bytes.compareAndSwapLong(0, 0L, 1L); + assertEquals(1L, bytes.readLong(0)); + } + + @Test + public void testRead() { for (int i = 0; i < bytes.capacity(); i++) bytes.writeByte(i, i); bytes.position(0); @@ -71,11 +80,10 @@ public class ByteBufferBytesTest { for (int i = (int) (bytes.capacity() - 1); i >= 0; i--) { assertEquals((byte) i, bytes.readByte(i)); } - } @Test - public void testReadFully() throws Exception { + public void testReadFully() { for (int i = 0; i < bytes.capacity(); i++) bytes.write(i); bytes.position(0); @@ -86,16 +94,15 @@ public class ByteBufferBytesTest { } @Test - public void testCompareAndSetLong() throws Exception { + public void testCompareAndSetLong() { assertTrue(bytes.compareAndSwapLong(0, 0, 1)); assertFalse(bytes.compareAndSwapLong(0, 0, 1)); assertTrue(bytes.compareAndSwapLong(8, 0, 1)); assertTrue(bytes.compareAndSwapLong(0, 1, 2)); - } @Test - public void testPosition() throws Exception { + public void testPosition() { for (int i = 0; i < bytes.capacity(); i++) bytes.write(i); for (int i = (int) (bytes.capacity() - 1); i >= 0; i--) { @@ -105,29 +112,23 @@ public class ByteBufferBytesTest { } @Test - public void testCapacity() throws Exception { - assertEquals(SIZE, bytes.capacity()); - assertEquals(10, new NativeBytes(0, 0, 10).capacity()); - } - - @Test - public void testRemaining() throws Exception { + public void testRemaining() { assertEquals(SIZE, bytes.remaining()); bytes.position(10); assertEquals(SIZE - 10, bytes.remaining()); } @Test - public void testByteOrder() throws Exception { + public void testByteOrder() { assertEquals(ByteOrder.nativeOrder(), bytes.byteOrder()); } @Test - public void testCheckEndOfBuffer() throws Exception { + public void testCheckEndOfBuffer() { bytes.checkEndOfBuffer(); - bytes.position(SIZE + 2); try { + bytes.position(SIZE + 2); bytes.checkEndOfBuffer(); fail(); } catch (IndexOutOfBoundsException expected) { @@ -146,11 +147,28 @@ public class ByteBufferBytesTest { } private void testAppendDouble0(double d) { - bytes.position(0); + bytes.clear(); bytes.append(d).append(' '); - bytes.position(0); + bytes.flip(); double d2 = bytes.parseDouble(); assertEquals(d, d2, 0); + + bytes.selfTerminating(true); + bytes.clear(); + bytes.append(d); + bytes.flip(); + double d3 = bytes.parseDouble(); + assertEquals(d, d3, 0); + + bytes.selfTerminating(false); + bytes.clear(); + bytes.append(d); + bytes.flip(); + try { + fail("got " + bytes.parseDouble()); + } catch (BufferUnderflowException expected) { + // expected + } } @Test @@ -175,7 +193,7 @@ public class ByteBufferBytesTest { bytes.position(0); bytes.append(d, precision).append(' '); bytes.position(0); - String text = bytes.parseUTF(SPACE_STOP); + String text = bytes.parseUtf8(SPACE_STOP); bytes.position(0); assertEquals(0, bytes.position()); double d2 = bytes.parseDouble(); @@ -252,19 +270,19 @@ public class ByteBufferBytesTest { bytes.append(word).append('\t'); } bytes.append('\t'); - bytes.position(0); + bytes.flip(); for (String word : words) { - assertEquals(word, bytes.parseUTF(CONTROL_STOP)); + assertEquals(word, bytes.parseUtf8(CONTROL_STOP)); } - assertEquals("", bytes.parseUTF(CONTROL_STOP)); + assertEquals("", bytes.parseUtf8(CONTROL_STOP)); bytes.position(0); StringBuilder sb = new StringBuilder(); for (String word : words) { - bytes.parseUTF(sb, CONTROL_STOP); + bytes.parseUtf8(sb, CONTROL_STOP); assertEquals(word, sb.toString()); } - bytes.parseUTF(sb, CONTROL_STOP); + bytes.parseUtf8(sb, CONTROL_STOP); assertEquals("", sb.toString()); bytes.position(0); @@ -272,10 +290,11 @@ public class ByteBufferBytesTest { assertEquals(6, bytes.position()); bytes.skipTo(CONTROL_STOP); assertEquals(13, bytes.position()); - bytes.skipTo(CONTROL_STOP); - assertEquals(17, bytes.position()); - bytes.skipTo(CONTROL_STOP); - assertEquals(18, bytes.position()); + assertTrue(bytes.skipTo(CONTROL_STOP)); + assertEquals(23, bytes.position()); + assertTrue(bytes.skipTo(CONTROL_STOP)); + assertEquals(24, bytes.position()); + assertFalse(bytes.skipTo(CONTROL_STOP)); bytes.position(0); bytes.stepBackAndSkipTo(CONTROL_STOP); @@ -285,7 +304,6 @@ public class ByteBufferBytesTest { bytes.position(10); bytes.stepBackAndSkipTo(CONTROL_STOP); assertEquals(13, bytes.position()); - } @Test @@ -562,8 +580,8 @@ public class ByteBufferBytesTest { SimpleDateFormat sdf = new SimpleDateFormat("yyyy/MM/dd'T'HH:mm:ss.SSS"); sdf.setTimeZone(TimeZone.getTimeZone("GMT")); String asStr = sdf.format(new Date(now)); - assertEquals(asStr, bytes.parseUTF(SPACE_STOP)); - assertEquals(asStr, bytes.parseUTF(SPACE_STOP)); + assertEquals(asStr, bytes.parseUtf8(SPACE_STOP)); + assertEquals(asStr, bytes.parseUtf8(SPACE_STOP)); } @Test @@ -636,7 +654,7 @@ public class ByteBufferBytesTest { public void testAppendSubstring() { bytes.append("Hello World", 2, 7).append("\n"); bytes.position(0); - assertEquals("Hello World".substring(2, 7), bytes.parseUTF(CONTROL_STOP)); + assertEquals("Hello World".substring(2, 7), bytes.parseUtf8(CONTROL_STOP)); } @Test @@ -658,18 +676,51 @@ public class ByteBufferBytesTest { bytes.append(1234).append(' '); bytes.append(123456L).append(' '); bytes.append(1.2345).append(' '); + bytes.append(1.5555, 3).append(' '); bytes.position(0); assertEquals(false, bytes.parseBoolean(SPACE_STOP)); assertEquals(true, bytes.parseBoolean(SPACE_STOP)); assertEquals(null, bytes.parseBoolean(SPACE_STOP)); - assertEquals("word£€", bytes.parseUTF(SPACE_STOP)); + assertEquals("word£€", bytes.parseUtf8(SPACE_STOP)); assertEquals(BuySell.Buy, bytes.parseEnum(BuySell.class, SPACE_STOP)); assertEquals(1234, bytes.parseLong()); assertEquals(123456L, bytes.parseLong()); assertEquals(1.2345, bytes.parseDouble(), 0); assertEquals(1.556, bytes.parseDouble(), 0); + } + @Test + public void testSelfTerminating() { + bytes.limit(0); + bytes.selfTerminating(true); + assertEquals(null, bytes.parseBoolean(ALL)); + assertEquals(0L, bytes.parseLong()); + assertEquals(0.0, bytes.parseDouble(), 0.0); + assertEquals("", bytes.parseUtf8(ALL)); + assertEquals(null, bytes.parseEnum(StopCharTesters.class, ALL)); + + bytes.selfTerminating(false); + try { + fail("got " + bytes.parseBoolean(ALL)); + } catch (BufferUnderflowException ignored) { + } + try { + fail("got " + bytes.parseLong()); + } catch (BufferUnderflowException ignored) { + } + try { + fail("got " + bytes.parseDouble()); + } catch (BufferUnderflowException ignored) { + } + try { + fail("got " + bytes.parseUtf8(ALL)); + } catch (BufferUnderflowException ignored) { + } + try { + fail("got " + bytes.parseEnum(StopCharTesters.class, ALL)); + } catch (BufferUnderflowException ignored) { + } } @Test @@ -690,7 +741,7 @@ public class ByteBufferBytesTest { bytes.write("good bye\n".getBytes(), 4, 4); bytes.write(4, "0 w".getBytes()); bytes.position(0); - assertEquals("Hell0 worl bye", bytes.parseUTF(CONTROL_STOP)); + assertEquals("Hell0 worl bye", bytes.parseUtf8(CONTROL_STOP)); } @Test @@ -698,7 +749,7 @@ public class ByteBufferBytesTest { bytes.append(Arrays.asList(1, 2, 3, 4, 5), ";").append(' '); bytes.append(new TreeSet<Integer>(Arrays.asList(21, 2, 13, 4, 5)), ";"); bytes.position(0); - assertEquals("1;2;3;4;5 2;4;5;13;21", bytes.parseUTF(CONTROL_STOP)); + assertEquals("1;2;3;4;5 2;4;5;13;21", bytes.parseUtf8(CONTROL_STOP)); } @Test @@ -723,14 +774,14 @@ public class ByteBufferBytesTest { bytes.position(0); bytes.parseDecimal(md2); bytes.position(0); - String text = bytes.parseUTF(CONTROL_STOP); + String text = bytes.parseUtf8(CONTROL_STOP); if (!md.equals(md2)) assertEquals("n: " + n + ", s: " + j + " t: " + text, md, md2); } @Test public void testStream() throws IOException { - bytes = new ByteBufferBytes(ByteBuffer.allocate(1000)); + bytes = ByteBufferBytes.wrap(ByteBuffer.allocate(1000)); GZIPOutputStream out = new GZIPOutputStream(bytes.outputStream()); out.write("Hello world\n".getBytes()); out.close(); @@ -777,7 +828,7 @@ public class ByteBufferBytesTest { @Test public void testWriteObject() { - for (Object o : new Object[]{10, 9.9, "string", new Date(), BigDecimal.valueOf(1.1)}) { + for (Object o : new Object[]{BigDecimal.valueOf(-1.234), 10, 9.9, "string", new Date(), BigDecimal.valueOf(1.1)}) { bytes.position(0); bytes.writeObject(o); // System.out.println(o +" size: "+bytes.position()); @@ -795,7 +846,7 @@ public class ByteBufferBytesTest { public void testWriteSerializable() { int capacity = 16 * 1024; byteBuffer = ByteBuffer.allocateDirect(capacity); - bytes = new ByteBufferBytes(byteBuffer); + bytes = ByteBufferBytes.wrap(byteBuffer); Calendar cal = Calendar.getInstance(); bytes.writeObject(cal); Dummy d = new Dummy(); @@ -820,6 +871,50 @@ public class ByteBufferBytesTest { assertEquals(11 * 11, bytes.readInt(4L)); } + @Test + public void testReadWriteMarshallable() { + // generate a class for this interface. + // you can use any hand written BytesMarshallable + MyMarshallable mm = DataValueClasses.newInstance(MyMarshallable.class); + + mm.setNum(5); + mm.setBig(3.1415); + mm.setText("Hello World"); + + // write to a byte[] + ByteBuffer byteBuffer = ByteBuffer.allocate(128); + IByteBufferBytes bbb = ByteBufferBytes.wrap(byteBuffer); + mm.writeMarshallable(bbb); + + // how much data was written. + int len = (int) bbb.position(); + byte[] bytes = byteBuffer.array(); + + // deserialize from a byte[] + MyMarshallable mm2 = DataValueClasses.newInstance(MyMarshallable.class); + IByteBufferBytes bbb2 = ByteBufferBytes.wrap(ByteBuffer.wrap(bytes)); + bbb2.limit(len); + mm2.readMarshallable(bbb2); + + assertEquals(5, mm2.getNum()); + assertEquals(3.1415, mm2.getBig(), 0.0); + assertEquals("Hello World", mm2.getText()); + } + + interface MyMarshallable extends BytesMarshallable { + int getNum(); + + void setNum(int num); + + double getBig(); + + void setBig(double d); + + String getText(); + + void setText(@MaxSize(16) String text); + } + enum BuySell { Buy, Sell } @@ -853,7 +948,7 @@ public class ByteBufferBytesTest { bytes.finish(); bytes.flush(); - bytes.reset(); + bytes.clear(); assertEquals(0, bytes.position()); assertEquals(8, bytes.skip(8)); assertEquals(8, bytes.position()); @@ -865,15 +960,15 @@ public class ByteBufferBytesTest { public void testWriteList() { List<Integer> ints = Arrays.asList(1, 2, 3, 4); bytes.writeList(ints); - bytes.reset(); + bytes.clear(); List<Integer> ints2 = new ArrayList<Integer>(); bytes.readList(ints2, Integer.class); assertEquals(ints, ints2); - bytes.reset(); + bytes.clear(); List<String> words = Arrays.asList("Hello word byte for now".split(" ")); bytes.writeList(words); - bytes.reset(); + bytes.clear(); List<String> words2 = new ArrayList<String>(); bytes.readList(words2, String.class); } @@ -892,7 +987,7 @@ public class ByteBufferBytesTest { bytes.writeMap(map); bytes.finish(); - bytes.reset(); + bytes.clear(); Map<String, Integer> map2 = new LinkedHashMap<String, Integer>(); bytes.readMap(map2, String.class, Integer.class); assertEquals(map, map2); @@ -904,7 +999,7 @@ public class ByteBufferBytesTest { ExecutorService es = Executors.newSingleThreadExecutor(new NamedThreadFactory("unloadFailed")); Future<Void> future = es.submit(new Callable<Void>() { @Override - public Void call() throws Exception { + public Void call() { bytes.unlockInt(0); return null; } @@ -917,4 +1012,26 @@ public class ByteBufferBytesTest { assertEquals(IllegalMonitorStateException.class, e.getCause().getClass()); } } + + @Test + public void testToString() { + NativeBytes bytes = new DirectStore(32).bytes(); + assertEquals("[pos: 0, lim: 32, cap: 32 ] ٠٠٠٠٠٠٠٠٠٠٠٠٠٠٠٠٠٠٠٠٠٠٠٠٠٠٠٠٠٠٠٠", bytes.toDebugString()); + bytes.writeByte(1); + assertEquals("[pos: 1, lim: 32, cap: 32 ] ⒈‖٠٠٠٠٠٠٠٠٠٠٠٠٠٠٠٠٠٠٠٠٠٠٠٠٠٠٠٠٠٠٠", bytes.toDebugString()); + bytes.writeByte(2); + assertEquals("[pos: 2, lim: 32, cap: 32 ] ⒈⒉‖٠٠٠٠٠٠٠٠٠٠٠٠٠٠٠٠٠٠٠٠٠٠٠٠٠٠٠٠٠٠", bytes.toDebugString()); + bytes.writeByte(3); + assertEquals("[pos: 3, lim: 32, cap: 32 ] ⒈⒉⒊‖٠٠٠٠٠٠٠٠٠٠٠٠٠٠٠٠٠٠٠٠٠٠٠٠٠٠٠٠٠", bytes.toDebugString()); + bytes.writeByte(4); + assertEquals("[pos: 4, lim: 32, cap: 32 ] ⒈⒉⒊⒋‖٠٠٠٠٠٠٠٠٠٠٠٠٠٠٠٠٠٠٠٠٠٠٠٠٠٠٠٠", bytes.toDebugString()); + bytes.writeByte(5); + assertEquals("[pos: 5, lim: 32, cap: 32 ] ⒈⒉⒊⒋⒌‖٠٠٠٠٠٠٠٠٠٠٠٠٠٠٠٠٠٠٠٠٠٠٠٠٠٠٠", bytes.toDebugString()); + bytes.writeByte(6); + assertEquals("[pos: 6, lim: 32, cap: 32 ] ⒈⒉⒊⒋⒌⒍‖٠٠٠٠٠٠٠٠٠٠٠٠٠٠٠٠٠٠٠٠٠٠٠٠٠٠", bytes.toDebugString()); + bytes.writeByte(7); + assertEquals("[pos: 7, lim: 32, cap: 32 ] ⒈⒉⒊⒋⒌⒍⒎‖٠٠٠٠٠٠٠٠٠٠٠٠٠٠٠٠٠٠٠٠٠٠٠٠٠", bytes.toDebugString()); + bytes.writeByte(8); + assertEquals("[pos: 8, lim: 32, cap: 32 ] ⒈⒉⒊⒋⒌⒍⒎⒏‖٠٠٠٠٠٠٠٠٠٠٠٠٠٠٠٠٠٠٠٠٠٠٠٠", bytes.toDebugString()); + } } diff --git a/lang/src/test/java/net/openhft/lang/io/BytesTest.java b/lang/src/test/java/net/openhft/lang/io/BytesTest.java new file mode 100755 index 0000000..e44c110 --- /dev/null +++ b/lang/src/test/java/net/openhft/lang/io/BytesTest.java @@ -0,0 +1,28 @@ +package net.openhft.lang.io; + +import org.junit.Test; + +import static org.junit.Assert.assertNotSame; +import static org.junit.Assert.assertSame; + +/** + * Created by peter.lawrey on 06/08/2015. + */ +public class BytesTest { + @Test + public void testReadEnum() { + Bytes b = DirectStore.allocate(128).bytes(); + b.writeEnum("Hello"); + b.writeEnum("World"); + b.flip(); + String x = b.readEnum(String.class); + String y = b.readEnum(String.class); + assertNotSame(x, y); + b.position(0); + String x2 = b.readEnum(String.class); + String y2 = b.readEnum(String.class); + assertNotSame(x2, y2); + assertSame(x, x2); + assertSame(y, y2); + } +} diff --git a/lang/src/test/java/net/openhft/lang/io/DirectByteBufferBytesTest.java b/lang/src/test/java/net/openhft/lang/io/DirectByteBufferBytesTest.java index bba0933..c3d35b6 100644..100755 --- a/lang/src/test/java/net/openhft/lang/io/DirectByteBufferBytesTest.java +++ b/lang/src/test/java/net/openhft/lang/io/DirectByteBufferBytesTest.java @@ -1,17 +1,17 @@ /* - * Copyright 2013 Peter Lawrey + * Copyright (C) 2015 higherfrequencytrading.com * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as published by + * the Free Software Foundation, either version 3 of the License. * - * http://www.apache.org/licenses/LICENSE-2.0 + * 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 Lesser General Public License for more details. * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. + * You should have received a copy of the GNU Lesser General Public License + * along with this program. If not, see <http://www.gnu.org/licenses/>. */ package net.openhft.lang.io; @@ -23,6 +23,7 @@ import org.junit.Test; import java.io.*; import java.math.BigDecimal; +import java.nio.BufferUnderflowException; import java.nio.ByteBuffer; import java.nio.ByteOrder; import java.text.SimpleDateFormat; @@ -31,8 +32,7 @@ import java.util.concurrent.*; import java.util.zip.GZIPInputStream; import java.util.zip.GZIPOutputStream; -import static net.openhft.lang.io.StopCharTesters.CONTROL_STOP; -import static net.openhft.lang.io.StopCharTesters.SPACE_STOP; +import static net.openhft.lang.io.StopCharTesters.*; import static org.junit.Assert.*; /** @@ -41,17 +41,17 @@ import static org.junit.Assert.*; */ public class DirectByteBufferBytesTest { public static final int SIZE = 128; - private ByteBufferBytes bytes; + private Bytes bytes; private ByteBuffer byteBuffer; @Before public void beforeTest() { byteBuffer = ByteBuffer.allocateDirect(SIZE).order(ByteOrder.nativeOrder()); - bytes = new ByteBufferBytes(byteBuffer); + bytes = ByteBufferBytes.wrap(byteBuffer); } @Test - public void testLongHash() throws Exception { + public void testLongHash() { byte[] bytes = {1, 2, 3, 4, 5, 6, 7, 8}; long h = NativeBytes.longHash(bytes, 0, bytes.length); assertFalse(h == 0); @@ -62,7 +62,7 @@ public class DirectByteBufferBytesTest { } @Test - public void testRead() throws Exception { + public void testRead() { for (int i = 0; i < bytes.capacity(); i++) bytes.writeByte(i, i); bytes.position(0); @@ -71,11 +71,10 @@ public class DirectByteBufferBytesTest { for (int i = (int) (bytes.capacity() - 1); i >= 0; i--) { assertEquals((byte) i, bytes.readByte(i)); } - } @Test - public void testReadFully() throws Exception { + public void testReadFully() { for (int i = 0; i < bytes.capacity(); i++) bytes.write(i); bytes.position(0); @@ -86,16 +85,15 @@ public class DirectByteBufferBytesTest { } @Test - public void testCompareAndSetLong() throws Exception { + public void testCompareAndSetLong() { assertTrue(bytes.compareAndSwapLong(0, 0, 1)); assertFalse(bytes.compareAndSwapLong(0, 0, 1)); assertTrue(bytes.compareAndSwapLong(8, 0, 1)); assertTrue(bytes.compareAndSwapLong(0, 1, 2)); - } @Test - public void testPosition() throws Exception { + public void testPosition() { for (int i = 0; i < bytes.capacity(); i++) bytes.write(i); for (int i = (int) (bytes.capacity() - 1); i >= 0; i--) { @@ -105,29 +103,23 @@ public class DirectByteBufferBytesTest { } @Test - public void testCapacity() throws Exception { - assertEquals(SIZE, bytes.capacity()); - assertEquals(10, new NativeBytes(0, 0, 10).capacity()); - } - - @Test - public void testRemaining() throws Exception { + public void testRemaining() { assertEquals(SIZE, bytes.remaining()); bytes.position(10); assertEquals(SIZE - 10, bytes.remaining()); } @Test - public void testByteOrder() throws Exception { + public void testByteOrder() { assertEquals(ByteOrder.nativeOrder(), bytes.byteOrder()); } @Test - public void testCheckEndOfBuffer() throws Exception { + public void testCheckEndOfBuffer() { bytes.checkEndOfBuffer(); - bytes.position(SIZE + 2); try { + bytes.position(SIZE + 2); bytes.checkEndOfBuffer(); fail(); } catch (IndexOutOfBoundsException expected) { @@ -175,7 +167,7 @@ public class DirectByteBufferBytesTest { bytes.position(0); bytes.append(d, precision).append(' '); bytes.position(0); - String text = bytes.parseUTF(SPACE_STOP); + String text = bytes.parseUtf8(SPACE_STOP); bytes.position(0); assertEquals(0, bytes.position()); double d2 = bytes.parseDouble(); @@ -252,19 +244,19 @@ public class DirectByteBufferBytesTest { bytes.append(word).append('\t'); } bytes.append('\t'); - bytes.position(0); + bytes.flip(); for (String word : words) { - assertEquals(word, bytes.parseUTF(CONTROL_STOP)); + assertEquals(word, bytes.parseUtf8(CONTROL_STOP)); } - assertEquals("", bytes.parseUTF(CONTROL_STOP)); + assertEquals("", bytes.parseUtf8(CONTROL_STOP)); bytes.position(0); StringBuilder sb = new StringBuilder(); for (String word : words) { - bytes.parseUTF(sb, CONTROL_STOP); + bytes.parseUtf8(sb, CONTROL_STOP); assertEquals(word, sb.toString()); } - bytes.parseUTF(sb, CONTROL_STOP); + bytes.parseUtf8(sb, CONTROL_STOP); assertEquals("", sb.toString()); bytes.position(0); @@ -272,10 +264,11 @@ public class DirectByteBufferBytesTest { assertEquals(6, bytes.position()); bytes.skipTo(CONTROL_STOP); assertEquals(13, bytes.position()); - bytes.skipTo(CONTROL_STOP); - assertEquals(17, bytes.position()); - bytes.skipTo(CONTROL_STOP); - assertEquals(18, bytes.position()); + assertTrue(bytes.skipTo(CONTROL_STOP)); + assertEquals(23, bytes.position()); + assertTrue(bytes.skipTo(CONTROL_STOP)); + assertEquals(24, bytes.position()); + assertFalse(bytes.skipTo(CONTROL_STOP)); bytes.position(0); bytes.stepBackAndSkipTo(CONTROL_STOP); @@ -285,7 +278,6 @@ public class DirectByteBufferBytesTest { bytes.position(10); bytes.stepBackAndSkipTo(CONTROL_STOP); assertEquals(13, bytes.position()); - } @Test @@ -562,8 +554,8 @@ public class DirectByteBufferBytesTest { SimpleDateFormat sdf = new SimpleDateFormat("yyyy/MM/dd'T'HH:mm:ss.SSS"); sdf.setTimeZone(TimeZone.getTimeZone("GMT")); String asStr = sdf.format(new Date(now)); - assertEquals(asStr, bytes.parseUTF(SPACE_STOP)); - assertEquals(asStr, bytes.parseUTF(SPACE_STOP)); + assertEquals(asStr, bytes.parseUtf8(SPACE_STOP)); + assertEquals(asStr, bytes.parseUtf8(SPACE_STOP)); } @Test @@ -636,7 +628,7 @@ public class DirectByteBufferBytesTest { public void testAppendSubstring() { bytes.append("Hello World", 2, 7).append("\n"); bytes.position(0); - assertEquals("Hello World".substring(2, 7), bytes.parseUTF(CONTROL_STOP)); + assertEquals("Hello World".substring(2, 7), bytes.parseUtf8(CONTROL_STOP)); } @Test @@ -658,18 +650,96 @@ public class DirectByteBufferBytesTest { bytes.append(1234).append(' '); bytes.append(123456L).append(' '); bytes.append(1.2345).append(' '); - bytes.append(1.5555, 3).append(' '); + + bytes.append(1.1234567891, 9).append(' '); + bytes.append(1.1234567).append(' '); bytes.position(0); assertEquals(false, bytes.parseBoolean(SPACE_STOP)); assertEquals(true, bytes.parseBoolean(SPACE_STOP)); assertEquals(null, bytes.parseBoolean(SPACE_STOP)); - assertEquals("word£€", bytes.parseUTF(SPACE_STOP)); + assertEquals("word£€", bytes.parseUtf8(SPACE_STOP)); assertEquals(BuySell.Buy, bytes.parseEnum(BuySell.class, SPACE_STOP)); assertEquals(1234, bytes.parseLong()); assertEquals(123456L, bytes.parseLong()); assertEquals(1.2345, bytes.parseDouble(), 0); - assertEquals(1.556, bytes.parseDouble(), 0); + assertEquals(1.123456789, bytes.parseDouble(), 0); + assertEquals(1.1234567, bytes.parseDouble(), 0); + } + + @Test + public void testSelfTerminating() { + bytes.limit(0); + bytes.selfTerminating(true); + assertEquals(null, bytes.parseBoolean(ALL)); + assertEquals(0L, bytes.parseLong()); + assertEquals(0.0, bytes.parseDouble(), 0.0); + assertEquals("", bytes.parseUtf8(ALL)); + assertEquals(null, bytes.parseEnum(StopCharTesters.class, ALL)); + + bytes.selfTerminating(false); + try { + fail("got " + bytes.parseBoolean(ALL)); + } catch (BufferUnderflowException ignored) { + } + try { + fail("got " + bytes.parseLong()); + } catch (BufferUnderflowException ignored) { + } + try { + fail("got " + bytes.parseDouble()); + } catch (BufferUnderflowException ignored) { + } + try { + fail("got " + bytes.parseUtf8(ALL)); + } catch (BufferUnderflowException ignored) { + } + try { + fail("got " + bytes.parseEnum(StopCharTesters.class, ALL)); + } catch (BufferUnderflowException ignored) { + } + } + + @Test + public void testAppendParseDouble0() { + bytes.append(1.123456789234, 7).append(' '); + bytes.append(1.123456789234, 7).append(' '); + bytes.position(0); + assertEquals(1.1234568, bytes.parseDouble(), 0); + assertEquals(1.1234568, bytes.parseDouble(), 0); + } + + @Test + public void testAppendParseDouble1() { + bytes.append(1.123456789); + bytes.position(0); + assertEquals(1.123456789, bytes.parseDouble(), 0); + } + @Test + public void testAppendParseDoubleWithPadding() { + bytes.append("qwertyuiop").append(' '); + bytes.append(1.123456789).append(' '); + bytes.append(123456L); + bytes.position(0); + assertEquals("qwertyuiop", bytes.parseUtf8(SPACE_STOP)); + assertEquals(1.123456789, bytes.parseDouble(), 0); + assertEquals(123456, bytes.parseLong()); + } + + @Test + public void testAppendParseDoubleWithPadding2() { + bytes.append(1.2345, 3).append(' '); + bytes.append(2.1234567891, 9).append(' '); + bytes.position(0); + assertEquals(1.235, bytes.parseDouble(), 0); + assertEquals(2.123456789, bytes.parseDouble(), 0); + } + + @Test + public void testAppendParseDouble2() { + bytes.append(1.1234).append(' '); + bytes.position(0); + assertEquals(1.1234, bytes.parseDouble(), 0); } @Test @@ -690,7 +760,7 @@ public class DirectByteBufferBytesTest { bytes.write("good bye\n".getBytes(), 4, 4); bytes.write(4, "0 w".getBytes()); bytes.position(0); - assertEquals("Hell0 worl bye", bytes.parseUTF(CONTROL_STOP)); + assertEquals("Hell0 worl bye", bytes.parseUtf8(CONTROL_STOP)); } @Test @@ -698,7 +768,7 @@ public class DirectByteBufferBytesTest { bytes.append(Arrays.asList(1, 2, 3, 4, 5), ";").append(' '); bytes.append(new TreeSet<Integer>(Arrays.asList(21, 2, 13, 4, 5)), ";"); bytes.position(0); - assertEquals("1;2;3;4;5 2;4;5;13;21", bytes.parseUTF(CONTROL_STOP)); + assertEquals("1;2;3;4;5 2;4;5;13;21", bytes.parseUtf8(CONTROL_STOP)); } @Test @@ -723,14 +793,14 @@ public class DirectByteBufferBytesTest { bytes.position(0); bytes.parseDecimal(md2); bytes.position(0); - String text = bytes.parseUTF(CONTROL_STOP); + String text = bytes.parseUtf8(CONTROL_STOP); if (!md.equals(md2)) assertEquals("n: " + n + ", s: " + j + " t: " + text, md, md2); } @Test public void testStream() throws IOException { - bytes = new ByteBufferBytes(ByteBuffer.allocateDirect(1000)); + bytes = ByteBufferBytes.wrap(ByteBuffer.allocateDirect(1000)); GZIPOutputStream out = new GZIPOutputStream(bytes.outputStream()); out.write("Hello world\n".getBytes()); out.close(); @@ -795,7 +865,7 @@ public class DirectByteBufferBytesTest { public void testWriteSerializable() { int capacity = 16 * 1024; byteBuffer = ByteBuffer.allocateDirect(capacity); - bytes = new ByteBufferBytes(byteBuffer); + bytes = ByteBufferBytes.wrap(byteBuffer); Calendar cal = Calendar.getInstance(); bytes.writeObject(cal); Dummy d = new Dummy(); @@ -854,7 +924,7 @@ public class DirectByteBufferBytesTest { bytes.finish(); bytes.flush(); - bytes.reset(); + bytes.clear(); assertEquals(0, bytes.position()); assertEquals(8, bytes.skip(8)); assertEquals(8, bytes.position()); @@ -866,15 +936,15 @@ public class DirectByteBufferBytesTest { public void testWriteList() { List<Integer> ints = Arrays.asList(1, 2, 3, 4); bytes.writeList(ints); - bytes.reset(); + bytes.clear(); List<Integer> ints2 = new ArrayList<Integer>(); bytes.readList(ints2, Integer.class); assertEquals(ints, ints2); - bytes.reset(); + bytes.clear(); List<String> words = Arrays.asList("Hello word byte for now".split(" ")); bytes.writeList(words); - bytes.reset(); + bytes.clear(); List<String> words2 = new ArrayList<String>(); bytes.readList(words2, String.class); } @@ -893,7 +963,7 @@ public class DirectByteBufferBytesTest { bytes.writeMap(map); bytes.finish(); - bytes.reset(); + bytes.clear(); Map<String, Integer> map2 = new LinkedHashMap<String, Integer>(); bytes.readMap(map2, String.class, Integer.class); assertEquals(map, map2); @@ -905,7 +975,7 @@ public class DirectByteBufferBytesTest { ExecutorService es = Executors.newSingleThreadExecutor(new NamedThreadFactory("unloadFailed")); Future<Void> future = es.submit(new Callable<Void>() { @Override - public Void call() throws Exception { + public Void call() { bytes.unlockInt(0); return null; } @@ -918,4 +988,68 @@ public class DirectByteBufferBytesTest { assertEquals(IllegalMonitorStateException.class, e.getCause().getClass()); } } + + @Test + public void testToString() { + NativeBytes bytes = new DirectStore(32).bytes(); + assertEquals("[pos: 0, lim: 32, cap: 32 ] ٠٠٠٠٠٠٠٠٠٠٠٠٠٠٠٠٠٠٠٠٠٠٠٠٠٠٠٠٠٠٠٠", bytes.toDebugString()); + bytes.writeByte(1); + assertEquals("[pos: 1, lim: 32, cap: 32 ] ⒈‖٠٠٠٠٠٠٠٠٠٠٠٠٠٠٠٠٠٠٠٠٠٠٠٠٠٠٠٠٠٠٠", bytes.toDebugString()); + bytes.writeByte(2); + assertEquals("[pos: 2, lim: 32, cap: 32 ] ⒈⒉‖٠٠٠٠٠٠٠٠٠٠٠٠٠٠٠٠٠٠٠٠٠٠٠٠٠٠٠٠٠٠", bytes.toDebugString()); + bytes.writeByte(3); + assertEquals("[pos: 3, lim: 32, cap: 32 ] ⒈⒉⒊‖٠٠٠٠٠٠٠٠٠٠٠٠٠٠٠٠٠٠٠٠٠٠٠٠٠٠٠٠٠", bytes.toDebugString()); + bytes.writeByte(4); + assertEquals("[pos: 4, lim: 32, cap: 32 ] ⒈⒉⒊⒋‖٠٠٠٠٠٠٠٠٠٠٠٠٠٠٠٠٠٠٠٠٠٠٠٠٠٠٠٠", bytes.toDebugString()); + bytes.writeByte(5); + assertEquals("[pos: 5, lim: 32, cap: 32 ] ⒈⒉⒊⒋⒌‖٠٠٠٠٠٠٠٠٠٠٠٠٠٠٠٠٠٠٠٠٠٠٠٠٠٠٠", bytes.toDebugString()); + bytes.writeByte(6); + assertEquals("[pos: 6, lim: 32, cap: 32 ] ⒈⒉⒊⒋⒌⒍‖٠٠٠٠٠٠٠٠٠٠٠٠٠٠٠٠٠٠٠٠٠٠٠٠٠٠", bytes.toDebugString()); + bytes.writeByte(7); + assertEquals("[pos: 7, lim: 32, cap: 32 ] ⒈⒉⒊⒋⒌⒍⒎‖٠٠٠٠٠٠٠٠٠٠٠٠٠٠٠٠٠٠٠٠٠٠٠٠٠", bytes.toDebugString()); + bytes.writeByte(8); + assertEquals("[pos: 8, lim: 32, cap: 32 ] ⒈⒉⒊⒋⒌⒍⒎⒏‖٠٠٠٠٠٠٠٠٠٠٠٠٠٠٠٠٠٠٠٠٠٠٠٠", bytes.toDebugString()); + } + + @Test + public void testResizeKeepData() { + DirectByteBufferBytes buffer = new DirectByteBufferBytes(16); + for (int i = 1; buffer.position() < buffer.capacity(); i++) { + buffer.writeInt(i); + } + + buffer.resize(32, true, true).position(0L); + for(int i=1;i <= 8; i++) { + if(i <= 4 ) { + assertEquals(i, buffer.readInt()); + + } else { + assertEquals(0, buffer.readInt()); + } + } + } + + @Test + public void testResizeDeleteData() { + DirectByteBufferBytes buffer = new DirectByteBufferBytes(16); + for (int i = 1; buffer.position() < buffer.capacity(); i++) { + buffer.writeInt(i); + } + + buffer.resize(32, true, false).position(0L); + + for(int i=1;i <= 8; i++) { + assertEquals(0, buffer.readInt()); + } + } + + @Test(expected = IllegalArgumentException.class) + public void testResizeExceptionData() { + DirectByteBufferBytes buffer = new DirectByteBufferBytes(16); + for (int i = 1; buffer.position() < buffer.capacity(); i++) { + buffer.writeInt(i); + } + + buffer.resize(4, true, true); + } } diff --git a/lang/src/test/java/net/openhft/lang/io/DirectBytesTest.java b/lang/src/test/java/net/openhft/lang/io/DirectBytesTest.java index 090b0b5..6724309 100755..100644 --- a/lang/src/test/java/net/openhft/lang/io/DirectBytesTest.java +++ b/lang/src/test/java/net/openhft/lang/io/DirectBytesTest.java @@ -1,22 +1,23 @@ /* - * Copyright 2013 Peter Lawrey + * Copyright (C) 2015 higherfrequencytrading.com * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as published by + * the Free Software Foundation, either version 3 of the License. * - * http://www.apache.org/licenses/LICENSE-2.0 + * 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 Lesser General Public License for more details. * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. + * You should have received a copy of the GNU Lesser General Public License + * along with this program. If not, see <http://www.gnu.org/licenses/>. */ package net.openhft.lang.io; -import org.jetbrains.annotations.NotNull; +import net.openhft.lang.model.constraints.NotNull; +import org.junit.Ignore; import org.junit.Test; import static org.junit.Assert.*; @@ -25,33 +26,12 @@ import static org.junit.Assert.*; * @author peter.lawrey */ public class DirectBytesTest { - @Test - public void testSimpleLock() { - DirectBytes bytes = new DirectStore(64).createSlice(); - bytes.writeLong(0, -1L); - assertFalse(bytes.tryLockLong(0)); - bytes.writeLong(0, 0L); - assertTrue(bytes.tryLockLong(0)); - long val1 = bytes.readLong(0); - assertTrue(bytes.tryLockLong(0)); - bytes.unlockLong(0); - assertEquals(val1, bytes.readLong(0)); - bytes.unlockLong(0); - assertEquals(0L, bytes.readLong(0)); - try { - bytes.unlockLong(0); - fail(); - } catch (IllegalMonitorStateException e) { - // expected. - } - } - private static void manyToggles(@NotNull DirectStore store1, int lockCount, int from, int to) { long id = Thread.currentThread().getId(); assertEquals(0, id >>> 24); System.out.println("Thread " + id); - DirectBytes slice1 = store1.createSlice(); + DirectBytes slice1 = store1.bytes(); int records = 64; for (int i = 0; i < lockCount; i += records) { @@ -70,6 +50,7 @@ public class DirectBytesTest { int toggle1 = slice1.readInt(4); if (toggle1 == from) { slice1.writeInt(4L, to); + } else { // noinspection AssignmentToForLoopParameter,AssignmentToForLoopParameter i--; @@ -85,7 +66,7 @@ public class DirectBytesTest { assertEquals(0, id >>> 32); System.out.println("Thread " + id); - DirectBytes slice1 = store1.createSlice(); + DirectBytes slice1 = store1.bytes(); int records = 64; for (int i = 0; i < lockCount; i += records) { @@ -104,6 +85,7 @@ public class DirectBytesTest { long toggle1 = slice1.readLong(8); if (toggle1 == from) { slice1.writeLong(8L, to); + } else { // noinspection AssignmentToForLoopParameter,AssignmentToForLoopParameter i--; @@ -115,11 +97,32 @@ public class DirectBytesTest { } @Test - public void testAllocate() throws Exception { + public void testSimpleLock() { + DirectBytes bytes = new DirectStore(64).bytes(); + bytes.writeLong(0, -1L); + assertFalse(bytes.tryLockLong(0)); + bytes.writeLong(0, 0L); + assertTrue(bytes.tryLockLong(0)); + long val1 = bytes.readLong(0); + assertTrue(bytes.tryLockLong(0)); + bytes.unlockLong(0); + assertEquals(val1, bytes.readLong(0)); + bytes.unlockLong(0); + assertEquals(0L, bytes.readLong(0)); + try { + bytes.unlockLong(0); + fail(); + } catch (IllegalMonitorStateException e) { + // expected. + } + } + + @Test + public void testAllocate() { long size = 1L << 24; // 31; don't overload cloud-bees DirectStore store = DirectStore.allocate(size); assertEquals(size, store.size()); - DirectBytes slice = store.createSlice(); + DirectBytes slice = store.bytes(); slice.positionAndSize(0, size); slice.writeLong(0, size); slice.writeLong(size - 8, size); @@ -175,7 +178,7 @@ public class DirectBytesTest { } @Test - public void testLocking2() throws Exception { + public void testLocking2() throws InterruptedException { if (Runtime.getRuntime().availableProcessors() < 2) { System.err.println("Test requires 2 CPUs, skipping"); return; @@ -193,8 +196,8 @@ public class DirectBytesTest { assertEquals(0, id >>> 24); int expected = (1 << 24) | (int) id; try { - DirectBytes slice1 = store1.createSlice(); - DirectBytes slice2 = store2.createSlice(); + DirectBytes slice1 = store1.bytes(); + DirectBytes slice2 = store2.bytes(); for (int i = 0; i < lockCount; i += 2) { slice1.busyLockInt(0); @@ -210,6 +213,7 @@ public class DirectBytesTest { slice1.writeInt(4, 0); // if (i % 10000== 0) // System.out.println("t: " + i); + } else { // noinspection AssignmentToForLoopParameter i--; @@ -219,6 +223,7 @@ public class DirectBytesTest { slice2.writeInt(4, 0); // if (i % 10000== 0) // System.out.println("t: " + i); + } else { // noinspection AssignmentToForLoopParameter i--; @@ -249,8 +254,8 @@ public class DirectBytesTest { int expected = (1 << 24) | (int) id; System.out.println("Thread " + id); - DirectBytes slice1 = store1.createSlice(); - DirectBytes slice2 = store2.createSlice(); + DirectBytes slice1 = store1.bytes(); + DirectBytes slice2 = store2.bytes(); for (int i = 0; i < lockCount; i += 2) { slice1.busyLockInt(0); slice2.busyLockInt(0); @@ -265,6 +270,7 @@ public class DirectBytesTest { slice1.writeInt(4, 1); // if (i % 10000== 0) // System.out.println("t: " + i); + } else { // noinspection AssignmentToForLoopParameter i--; @@ -274,6 +280,7 @@ public class DirectBytesTest { slice2.writeInt(4, 1); // if (i % 10000== 0) // System.out.println("t: " + i); + } else { // noinspection AssignmentToForLoopParameter i--; @@ -297,4 +304,22 @@ public class DirectBytesTest { store2.free(); } + @Test + @Ignore + public void benchmarkByteLargeArray() { + long length = 1L << 32; + long start = System.nanoTime(); + DirectBytes array = DirectStore.allocateLazy(length).bytes(); + System.out.println("Constructor time: " + (System.nanoTime() - start) / 1e9 + " sec"); + int iters = 2; + byte one = 1; + start = System.nanoTime(); + for (int it = 0; it < iters; it++) { + for (long i = 0; i < length; i++) { + array.writeByte(i, one); + array.writeByte(i, (byte) (array.readByte(i) + one)); + } + } + System.out.println("Computation time: " + (System.nanoTime() - start) / 1e9 / iters + " sec"); + } } diff --git a/lang/src/test/java/net/openhft/lang/io/IOToolsTest.java b/lang/src/test/java/net/openhft/lang/io/IOToolsTest.java index 086749d..163c074 100644 --- a/lang/src/test/java/net/openhft/lang/io/IOToolsTest.java +++ b/lang/src/test/java/net/openhft/lang/io/IOToolsTest.java @@ -1,17 +1,17 @@ /* - * Copyright 2013 Peter Lawrey + * Copyright (C) 2015 higherfrequencytrading.com * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as published by + * the Free Software Foundation, either version 3 of the License. * - * http://www.apache.org/licenses/LICENSE-2.0 + * 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 Lesser General Public License for more details. * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. + * You should have received a copy of the GNU Lesser General Public License + * along with this program. If not, see <http://www.gnu.org/licenses/>. */ package net.openhft.lang.io; diff --git a/lang/src/test/java/net/openhft/lang/io/LockingViaFileLockMain.java b/lang/src/test/java/net/openhft/lang/io/LockingViaFileLockMain.java index 46abc86..93fe4b6 100644 --- a/lang/src/test/java/net/openhft/lang/io/LockingViaFileLockMain.java +++ b/lang/src/test/java/net/openhft/lang/io/LockingViaFileLockMain.java @@ -1,17 +1,17 @@ /* - * Copyright 2013 Peter Lawrey + * Copyright (C) 2015 higherfrequencytrading.com * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as published by + * the Free Software Foundation, either version 3 of the License. * - * http://www.apache.org/licenses/LICENSE-2.0 + * 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 Lesser General Public License for more details. * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. + * You should have received a copy of the GNU Lesser General Public License + * along with this program. If not, see <http://www.gnu.org/licenses/>. */ package net.openhft.lang.io; @@ -19,7 +19,6 @@ package net.openhft.lang.io; import java.io.File; import java.io.IOException; import java.io.RandomAccessFile; -import java.nio.ByteOrder; import java.nio.MappedByteBuffer; import java.nio.channels.FileChannel; import java.nio.channels.FileLock; @@ -29,7 +28,7 @@ import java.nio.channels.FileLock; * Date: 22/12/13 * Time: 11:05 * - * Toggled 10,000,128 times with an average delay of 2,402 ns + * <p>Toggled 10,000,128 times with an average delay of 2,402 ns */ public class LockingViaFileLockMain { static int RECORDS = Integer.getInteger("records", 128); @@ -46,7 +45,7 @@ public class LockingViaFileLockMain { File tmpFile = new File(System.getProperty("java.io.tmpdir"), "lock-test.dat"); FileChannel fc = new RandomAccessFile(tmpFile, "rw").getChannel(); MappedByteBuffer mbb = fc.map(FileChannel.MapMode.READ_WRITE, 0, RECORDS * RECORD_SIZE); - ByteBufferBytes bytes = new ByteBufferBytes(mbb.order(ByteOrder.nativeOrder())); + ByteBufferBytes bytes = new ByteBufferBytes(mbb); long start = 0; for (int i = -WARMUP / RECORDS; i < (RUNS + RECORDS - 1) / RECORDS; i++) { @@ -61,6 +60,7 @@ public class LockingViaFileLockMain { if (t == 0) if (i >= 0) { throw new AssertionError("Didn't toggle in time !??"); + } else { System.out.println("waiting"); t = 99999; diff --git a/lang/src/test/java/net/openhft/lang/io/LockingViaMMapMain.java b/lang/src/test/java/net/openhft/lang/io/LockingViaMMapMain.java index a1cddd1..7b57ff5 100644 --- a/lang/src/test/java/net/openhft/lang/io/LockingViaMMapMain.java +++ b/lang/src/test/java/net/openhft/lang/io/LockingViaMMapMain.java @@ -1,17 +1,17 @@ /* - * Copyright 2013 Peter Lawrey + * Copyright (C) 2015 higherfrequencytrading.com * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as published by + * the Free Software Foundation, either version 3 of the License. * - * http://www.apache.org/licenses/LICENSE-2.0 + * 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 Lesser General Public License for more details. * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. + * You should have received a copy of the GNU Lesser General Public License + * along with this program. If not, see <http://www.gnu.org/licenses/>. */ package net.openhft.lang.io; @@ -19,7 +19,6 @@ package net.openhft.lang.io; import java.io.File; import java.io.IOException; import java.io.RandomAccessFile; -import java.nio.ByteOrder; import java.nio.MappedByteBuffer; import java.nio.channels.FileChannel; @@ -28,13 +27,13 @@ import java.nio.channels.FileChannel; * Date: 22/12/13 * Time: 11:05 * - * Toggled 10,000,128 times with an average delay of 28 ns + * <p>Toggled 100,000,000 times with an average delay of 49 ns */ public class LockingViaMMapMain { static int RECORDS = Integer.getInteger("records", 128); static int RECORD_SIZE = Integer.getInteger("record_size", 64); // cache line size static int WARMUP = Integer.getInteger("warmup", RECORDS * 100); - static int RUNS = Integer.getInteger("runs", 5 * 1000 * 1000); + static int RUNS = Integer.getInteger("runs", 50 * 1000 * 1000); // offsets static int LOCK = 0; @@ -46,8 +45,7 @@ public class LockingViaMMapMain { File tmpFile = new File(System.getProperty("java.io.tmpdir"), "lock-test.dat"); FileChannel fc = new RandomAccessFile(tmpFile, "rw").getChannel(); MappedByteBuffer mbb = fc.map(FileChannel.MapMode.READ_WRITE, 0, RECORDS * RECORD_SIZE); - ByteBufferBytes bytes = new ByteBufferBytes(mbb.order(ByteOrder.nativeOrder())); - bytes.setCurrentThread(); + Bytes bytes = ByteBufferBytes.wrap(mbb); long start = 0; for (int i = -WARMUP / RECORDS; i < (RUNS + RECORDS - 1) / RECORDS; i++) { @@ -62,6 +60,7 @@ public class LockingViaMMapMain { if (t == 0) if (i >= 0) { throw new AssertionError("Didn't toggle in time !??"); + } else { Thread.sleep(200); } @@ -90,7 +89,7 @@ public class LockingViaMMapMain { long time = System.nanoTime() - start; final int toggles = (RUNS + RECORDS - 1) / RECORDS * RECORDS * 2; // one for each of two processes. System.out.printf("Toggled %,d times with an average delay of %,d ns%n", - toggles, time / toggles); + toggles, 2 * time / toggles); fc.close(); tmpFile.deleteOnExit(); } diff --git a/lang/src/test/java/net/openhft/lang/io/MappedFileTest.java b/lang/src/test/java/net/openhft/lang/io/MappedFileTest.java index 6456888..7b3a314 100644 --- a/lang/src/test/java/net/openhft/lang/io/MappedFileTest.java +++ b/lang/src/test/java/net/openhft/lang/io/MappedFileTest.java @@ -1,17 +1,17 @@ /* - * Copyright 2013 Peter Lawrey + * Copyright (C) 2015 higherfrequencytrading.com * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as published by + * the Free Software Foundation, either version 3 of the License. * - * http://www.apache.org/licenses/LICENSE-2.0 + * 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 Lesser General Public License for more details. * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. + * You should have received a copy of the GNU Lesser General Public License + * along with this program. If not, see <http://www.gnu.org/licenses/>. */ package net.openhft.lang.io; @@ -34,7 +34,6 @@ public class MappedFileTest { br.close(); throw ioe; } - } public static void delete(File file) throws IOException { @@ -69,8 +68,9 @@ public class MappedFileTest { fill(map1.buffer().force()); long free1 = dir.getFreeSpace(); - map1.release(); - map0.release(); + mfile.release(map1); + mfile.release(map0); + mfile.close(); // printMappings(); @@ -85,8 +85,6 @@ public class MappedFileTest { Thread.sleep(500); } assertTrue("free3-free1: " + (free3 - free1), free3 > free1); - - } private void fill(MappedByteBuffer buffer) { diff --git a/lang/src/test/java/net/openhft/lang/io/MappedStoreTest.java b/lang/src/test/java/net/openhft/lang/io/MappedStoreTest.java new file mode 100755 index 0000000..c93cc24 --- /dev/null +++ b/lang/src/test/java/net/openhft/lang/io/MappedStoreTest.java @@ -0,0 +1,143 @@ +/* + * Copyright (C) 2015 higherfrequencytrading.com + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as published by + * the Free Software Foundation, either version 3 of the License. + * + * 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 Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with this program. If not, see <http://www.gnu.org/licenses/>. + */ + +package net.openhft.lang.io; + +import net.openhft.lang.Jvm; +import net.openhft.lang.io.serialization.JDKZObjectSerializer; +import org.junit.After; +import org.junit.Test; + +import java.io.File; +import java.io.IOException; +import java.nio.channels.FileChannel; + +import static org.junit.Assert.assertEquals; + +public class MappedStoreTest { + //private static final long MS_SIZE = 3L << 30; + private static final long MS_SIZE = 1024; + + static File getStoreFile(String fileName) { + File file = new File(System.getProperty("java.io.tmpdir"), fileName); + file.delete(); + file.deleteOnExit(); + + return file; + } + + @After + public void tearDown() { + System.gc(); + } + + @Test + public void testCreateSlice() throws IOException { + File file = new File(System.getProperty("java.io.tmpdir") + "/MappedStoreTest-testCreateSlice" + System.nanoTime() + ".tmp"); + file.deleteOnExit(); + long size = Jvm.is64Bit() ? 3L << 30 : 256 << 20; + MappedStore ms = new MappedStore(file, FileChannel.MapMode.READ_WRITE, size); + DirectBytes slice = ms.bytes(); + assertEquals(1, slice.refCount()); + assertEquals(0L, slice.readLong(0L)); + assertEquals(0L, slice.readLong(ms.size() - 8)); + + slice.writeLong(0,1L); + assertEquals(1L,slice.readLong(0)); + + slice.release(); + + ms.close(); + } + + @Test + public void testOpenExistingFile() throws IOException { + File file = getStoreFile("mapped-store-2.tmp"); + + { + MappedStore ms1 = new MappedStore(file, FileChannel.MapMode.READ_WRITE, MS_SIZE); + DirectBytes slice1 = ms1.bytes(); + assertEquals(1, slice1.refCount()); + + slice1.writeLong(1L); + slice1.writeLong(2L); + slice1.release(); + + ms1.close(); + } + + { + MappedStore ms2 = new MappedStore(file, FileChannel.MapMode.READ_WRITE, MS_SIZE); + DirectBytes slice2 = ms2.bytes(); + assertEquals( 1, slice2.refCount()); + assertEquals(1L, slice2.readLong()); + assertEquals(2L, slice2.readLong()); + + slice2.release(); + + ms2.close(); + } + } + + /* + @Test + public void testSliceSize() { + File file = getStoreFile("mapped-store"); + + MappedStore ms = new MappedStore(file, FileChannel.MapMode.READ_WRITE, MS_SIZE); + DirectBytes slice = ms.bytes(); + + for(long i=0;i<MS_SIZE+1;i += 8) { + slice.writeLong(i); + } + + slice.release(); + ms.free(); + } + */ + // ************************************************************************* + // Helpers + // ************************************************************************* + + @Test + public void testCreateMappedStoreWithOffset() throws IOException { + final int _64k = 64 * 1024, _128k = 128 * 1024; + + File file = getStoreFile("mapped-store-3.tmp"); + fill(file, _128k); + + MappedStore ms = new MappedStore(file, FileChannel.MapMode.READ_WRITE, + _64k, _128k, JDKZObjectSerializer.INSTANCE); + Bytes bytes = ms.bytes(); + + assertEquals(1, bytes.readByte(1)); + assertEquals(0, bytes.readByte(_64k)); + + bytes.release(); + ms.close(); + } + + private void fill(File file, int expectedSize) throws IOException { + MappedStore ms = new MappedStore(file, FileChannel.MapMode.READ_WRITE, expectedSize); + Bytes bytes = ms.bytes(); + for (int i = 0; i < expectedSize; ++i) { + bytes.writeUnsignedByte(i); + } + bytes.release(); + ms.close(); + } +} + diff --git a/lang/src/test/java/net/openhft/lang/io/MutableDecimalTest.java b/lang/src/test/java/net/openhft/lang/io/MutableDecimalTest.java index ba6796e..055a7b7 100644 --- a/lang/src/test/java/net/openhft/lang/io/MutableDecimalTest.java +++ b/lang/src/test/java/net/openhft/lang/io/MutableDecimalTest.java @@ -1,17 +1,17 @@ /* - * Copyright 2013 Peter Lawrey + * Copyright (C) 2015 higherfrequencytrading.com * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as published by + * the Free Software Foundation, either version 3 of the License. * - * http://www.apache.org/licenses/LICENSE-2.0 + * 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 Lesser General Public License for more details. * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. + * You should have received a copy of the GNU Lesser General Public License + * along with this program. If not, see <http://www.gnu.org/licenses/>. */ package net.openhft.lang.io; @@ -36,7 +36,11 @@ public class MutableDecimalTest { assertEquals(Long.MAX_VALUE / 1e10, md2.doubleValue(), 0); assertEquals("922337203.6854775807", md2.toString()); - MutableDecimal md3 = new MutableDecimal(Math.PI, 6); + MutableDecimal md2b = new MutableDecimal((double) Long.MAX_VALUE, 10); + assertEquals(Long.MAX_VALUE / 1e10, md2b.doubleValue(), 0); + assertEquals("922337203.6854775807", md2b.toString()); + + MutableDecimal md3 = new MutableDecimal(Math.PI * Math.pow(10, 6), 6); assertEquals(3.141593, md3.doubleValue(), 0); assertEquals(3.141593f, md3.floatValue(), 0); assertEquals(3, md3.intValue()); diff --git a/lang/src/test/java/net/openhft/lang/io/NativeBytesTest.java b/lang/src/test/java/net/openhft/lang/io/NativeBytesTest.java index 2b50825..bcfa95b 100644..100755 --- a/lang/src/test/java/net/openhft/lang/io/NativeBytesTest.java +++ b/lang/src/test/java/net/openhft/lang/io/NativeBytesTest.java @@ -1,22 +1,23 @@ /* - * Copyright 2013 Peter Lawrey + * Copyright (C) 2015 higherfrequencytrading.com * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as published by + * the Free Software Foundation, either version 3 of the License. * - * http://www.apache.org/licenses/LICENSE-2.0 + * 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 Lesser General Public License for more details. * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. + * You should have received a copy of the GNU Lesser General Public License + * along with this program. If not, see <http://www.gnu.org/licenses/>. */ package net.openhft.lang.io; import net.openhft.lang.Maths; +import net.openhft.lang.model.Byteable; import net.openhft.lang.thread.NamedThreadFactory; import org.junit.Before; import org.junit.Test; @@ -24,6 +25,7 @@ import sun.nio.ch.DirectBuffer; import java.io.*; import java.math.BigDecimal; +import java.nio.BufferUnderflowException; import java.nio.ByteBuffer; import java.nio.ByteOrder; import java.text.SimpleDateFormat; @@ -32,8 +34,9 @@ import java.util.concurrent.*; import java.util.zip.GZIPInputStream; import java.util.zip.GZIPOutputStream; -import static net.openhft.lang.io.StopCharTesters.CONTROL_STOP; -import static net.openhft.lang.io.StopCharTesters.SPACE_STOP; +import static net.openhft.lang.io.StopCharTesters.*; +import static net.openhft.lang.model.DataValueClasses.newDirectInstance; +import static net.openhft.lang.model.DataValueClasses.newDirectReference; import static org.junit.Assert.*; /** @@ -49,11 +52,11 @@ public class NativeBytesTest { public void beforeTest() { byteBuffer = ByteBuffer.allocateDirect(SIZE); long addr = ((DirectBuffer) byteBuffer).address(); - bytes = new NativeBytes(addr, addr, addr + SIZE); + bytes = new NativeBytes(addr, addr + SIZE); } @Test - public void testLongHash() throws Exception { + public void testLongHash() { byte[] bytes = {1, 2, 3, 4, 5, 6, 7, 8}; long h = NativeBytes.longHash(bytes, 0, bytes.length); assertFalse(h == 0); @@ -64,7 +67,7 @@ public class NativeBytesTest { } @Test - public void testRead() throws Exception { + public void testRead() { for (int i = 0; i < bytes.capacity(); i++) bytes.writeByte(i, i); bytes.position(0); @@ -73,11 +76,10 @@ public class NativeBytesTest { for (int i = (int) (bytes.capacity() - 1); i >= 0; i--) { assertEquals((byte) i, bytes.readByte(i)); } - } @Test - public void testReadFully() throws Exception { + public void testReadFully() { for (int i = 0; i < bytes.capacity(); i++) bytes.write(i); bytes.position(0); @@ -88,16 +90,15 @@ public class NativeBytesTest { } @Test - public void testCompareAndSetLong() throws Exception { + public void testCompareAndSetLong() { assertTrue(bytes.compareAndSwapLong(0, 0, 1)); assertFalse(bytes.compareAndSwapLong(0, 0, 1)); assertTrue(bytes.compareAndSwapLong(8, 0, 1)); assertTrue(bytes.compareAndSwapLong(0, 1, 2)); - } @Test - public void testPosition() throws Exception { + public void testPosition() { for (int i = 0; i < bytes.capacity(); i++) bytes.write(i); for (int i = (int) (bytes.capacity() - 1); i >= 0; i--) { @@ -107,31 +108,32 @@ public class NativeBytesTest { } @Test - public void testCapacity() throws Exception { + public void testCapacity() { assertEquals(SIZE, bytes.capacity()); - assertEquals(10, new NativeBytes(0, 0, 10).capacity()); + assertEquals(10, new NativeBytes(100000, 100010).capacity()); } @Test - public void testRemaining() throws Exception { + public void testRemaining() { assertEquals(SIZE, bytes.remaining()); bytes.position(10); assertEquals(SIZE - 10, bytes.remaining()); } @Test - public void testByteOrder() throws Exception { + public void testByteOrder() { assertEquals(ByteOrder.nativeOrder(), bytes.byteOrder()); } @Test - public void testCheckEndOfBuffer() throws Exception { + public void testCheckEndOfBuffer() { bytes.checkEndOfBuffer(); - bytes.position(SIZE + 2); try { + bytes.position(SIZE + 2); bytes.checkEndOfBuffer(); fail(); + } catch (IllegalArgumentException expected) { } catch (IndexOutOfBoundsException expected) { } } @@ -148,11 +150,28 @@ public class NativeBytesTest { } private void testAppendDouble0(double d) { - bytes.position(0); + bytes.clear(); bytes.append(d).append(' '); - bytes.position(0); + bytes.flip(); double d2 = bytes.parseDouble(); assertEquals(d, d2, 0); + + bytes.selfTerminating(true); + bytes.clear(); + bytes.append(d); + bytes.flip(); + double d3 = bytes.parseDouble(); + assertEquals(d, d3, 0); + + bytes.selfTerminating(false); + bytes.clear(); + bytes.append(d); + bytes.flip(); + try { + fail("got " + bytes.parseDouble()); + } catch (BufferUnderflowException expected) { + // expected + } } @Test @@ -177,7 +196,7 @@ public class NativeBytesTest { bytes.position(0); bytes.append(d, precision).append(' '); bytes.position(0); - String text = bytes.parseUTF(SPACE_STOP); + String text = bytes.parseUtf8(SPACE_STOP); bytes.position(0); assertEquals(0, bytes.position()); double d2 = bytes.parseDouble(); @@ -199,7 +218,7 @@ public class NativeBytesTest { this.bytes.write(22, bytes); byte[] bytes3 = new byte[bytes.length]; - this.bytes.skipBytes((int) (22 - this.bytes.position())); + this.bytes.position(22); assertEquals(bytes3.length, this.bytes.read(bytes3)); assertTrue(Arrays.equals(bytes, bytes3)); this.bytes.position(this.bytes.capacity()); @@ -254,19 +273,19 @@ public class NativeBytesTest { bytes.append(word).append('\t'); } bytes.append('\t'); - bytes.position(0); + bytes.flip(); for (String word : words) { - assertEquals(word, bytes.parseUTF(CONTROL_STOP)); + assertEquals(word, bytes.parseUtf8(CONTROL_STOP)); } - assertEquals("", bytes.parseUTF(CONTROL_STOP)); + assertEquals("", bytes.parseUtf8(CONTROL_STOP)); bytes.position(0); StringBuilder sb = new StringBuilder(); for (String word : words) { - bytes.parseUTF(sb, CONTROL_STOP); + bytes.parseUtf8(sb, CONTROL_STOP); assertEquals(word, sb.toString()); } - bytes.parseUTF(sb, CONTROL_STOP); + bytes.parseUtf8(sb, CONTROL_STOP); assertEquals("", sb.toString()); bytes.position(0); @@ -274,10 +293,11 @@ public class NativeBytesTest { assertEquals(6, bytes.position()); bytes.skipTo(CONTROL_STOP); assertEquals(13, bytes.position()); - bytes.skipTo(CONTROL_STOP); - assertEquals(17, bytes.position()); - bytes.skipTo(CONTROL_STOP); - assertEquals(18, bytes.position()); + assertTrue(bytes.skipTo(CONTROL_STOP)); + assertEquals(23, bytes.position()); + assertTrue(bytes.skipTo(CONTROL_STOP)); + assertEquals(24, bytes.position()); + assertFalse(bytes.skipTo(CONTROL_STOP)); bytes.position(0); bytes.stepBackAndSkipTo(CONTROL_STOP); @@ -287,7 +307,6 @@ public class NativeBytesTest { bytes.position(10); bytes.stepBackAndSkipTo(CONTROL_STOP); assertEquals(13, bytes.position()); - } @Test @@ -399,7 +418,7 @@ public class NativeBytesTest { @Test public void testReadWriteStop() { - long[] longs = {Long.MIN_VALUE, Long.MAX_VALUE, Integer.MIN_VALUE, Integer.MAX_VALUE}; + long[] longs = {Long.MIN_VALUE, Long.MAX_VALUE, 1<<14, (1<<14)-1, 1<<7, (1<<7)-1, Integer.MIN_VALUE, Integer.MAX_VALUE}; for (long i : longs) { bytes.writeStopBit(i); // System.out.println(i + " " + bytes.position()); @@ -409,6 +428,18 @@ public class NativeBytesTest { bytes.position(0); for (long i : longs) assertEquals(i, bytes.readStopBit()); + + for(long l = 1; l > 0; l += l) { + bytes.clear(); + bytes.writeStopBit(l-1); + bytes.writeStopBit(l); + bytes.writeStopBit(l+1); + bytes.flip(); + assertEquals(l-1, bytes.readStopBit()); + assertEquals(l, bytes.readStopBit()); + assertEquals(l+1, bytes.readStopBit()); + assertEquals(0, bytes.remaining()); + } } @Test @@ -564,8 +595,8 @@ public class NativeBytesTest { SimpleDateFormat sdf = new SimpleDateFormat("yyyy/MM/dd'T'HH:mm:ss.SSS"); sdf.setTimeZone(TimeZone.getTimeZone("GMT")); String asStr = sdf.format(new Date(now)); - assertEquals(asStr, bytes.parseUTF(SPACE_STOP)); - assertEquals(asStr, bytes.parseUTF(SPACE_STOP)); + assertEquals(asStr, bytes.parseUtf8(SPACE_STOP)); + assertEquals(asStr, bytes.parseUtf8(SPACE_STOP)); } @Test @@ -638,7 +669,7 @@ public class NativeBytesTest { public void testAppendSubstring() { bytes.append("Hello World", 2, 7).append("\n"); bytes.position(0); - assertEquals("Hello World".substring(2, 7), bytes.parseUTF(CONTROL_STOP)); + assertEquals("Hello World".substring(2, 7), bytes.parseUtf8(CONTROL_STOP)); } @Test @@ -665,13 +696,45 @@ public class NativeBytesTest { assertEquals(false, bytes.parseBoolean(SPACE_STOP)); assertEquals(true, bytes.parseBoolean(SPACE_STOP)); assertEquals(null, bytes.parseBoolean(SPACE_STOP)); - assertEquals("word£€", bytes.parseUTF(SPACE_STOP)); + assertEquals("word£€", bytes.parseUtf8(SPACE_STOP)); assertEquals(BuySell.Buy, bytes.parseEnum(BuySell.class, SPACE_STOP)); assertEquals(1234, bytes.parseLong()); assertEquals(123456L, bytes.parseLong()); assertEquals(1.2345, bytes.parseDouble(), 0); assertEquals(1.556, bytes.parseDouble(), 0); + } + @Test + public void testSelfTerminating() { + bytes.limit(0); + bytes.selfTerminating(true); + assertEquals(null, bytes.parseBoolean(ALL)); + assertEquals(0L, bytes.parseLong()); + assertEquals(0.0, bytes.parseDouble(), 0.0); + assertEquals("", bytes.parseUtf8(ALL)); + assertEquals(null, bytes.parseEnum(StopCharTesters.class, ALL)); + + bytes.selfTerminating(false); + try { + fail("got " + bytes.parseBoolean(ALL)); + } catch (BufferUnderflowException ignored) { + } + try { + fail("got " + bytes.parseLong()); + } catch (BufferUnderflowException ignored) { + } + try { + fail("got " + bytes.parseDouble()); + } catch (BufferUnderflowException ignored) { + } + try { + fail("got " + bytes.parseUtf8(ALL)); + } catch (BufferUnderflowException ignored) { + } + try { + fail("got " + bytes.parseEnum(StopCharTesters.class, ALL)); + } catch (BufferUnderflowException ignored) { + } } @Test @@ -690,9 +753,10 @@ public class NativeBytesTest { public void testWriteBytes() { bytes.write("Hello World\n".getBytes(), 0, 10); bytes.write("good bye\n".getBytes(), 4, 4); - bytes.write(4, "0 w".getBytes()); + bytes.write(4, "0".getBytes()); + bytes.write(5, " w".getBytes(), 0, 2); bytes.position(0); - assertEquals("Hell0 worl bye", bytes.parseUTF(CONTROL_STOP)); + assertEquals("Hell0 worl bye", bytes.parseUtf8(CONTROL_STOP)); } @Test @@ -700,7 +764,7 @@ public class NativeBytesTest { bytes.append(Arrays.asList(1, 2, 3, 4, 5), ";").append(' '); bytes.append(new TreeSet<Integer>(Arrays.asList(21, 2, 13, 4, 5)), ";"); bytes.position(0); - assertEquals("1;2;3;4;5 2;4;5;13;21", bytes.parseUTF(CONTROL_STOP)); + assertEquals("1;2;3;4;5 2;4;5;13;21", bytes.parseUtf8(CONTROL_STOP)); } @Test @@ -725,7 +789,7 @@ public class NativeBytesTest { bytes.position(0); bytes.parseDecimal(md2); bytes.position(0); - String text = bytes.parseUTF(CONTROL_STOP); + String text = bytes.parseUtf8(CONTROL_STOP); if (!md.equals(md2)) assertEquals("n: " + n + ", s: " + j + " t: " + text, md, md2); } @@ -777,6 +841,43 @@ public class NativeBytesTest { } @Test + public void testWriteByteable() { + final DummyByteable db1w = newDirectInstance(DummyByteable.class); + final DummyByteable db2w = newDirectInstance(DummyByteable.class); + final DummyByteable db1r = newDirectReference(DummyByteable.class); + final DummyByteable db2r = newDirectReference(DummyByteable.class); + + db1w.setField1(1); + db1w.setField2(2); + db1w.setField3(3); + + db2w.setField1(4); + db2w.setField2(5); + db2w.setField3(6); + + bytes.write(db1w); + bytes.write(db2w); + + assertEquals(32, bytes.position()); + + db1r.bytes(bytes, 0); + db2r.bytes(bytes, db1w.maxSize()); + + assertEquals(1, db1r.getField1()); + assertEquals(2, db1r.getField2()); + assertEquals(3, db1r.getField3()); + assertEquals(4, db2r.getField1()); + assertEquals(5, db2r.getField2()); + assertEquals(6, db2r.getField3()); + } + + @Test(expected = IllegalArgumentException.class) + public void testWriteByteableException() { + DummyByteable byteable = newDirectReference(DummyByteable.class); + bytes.write(byteable); + } + + @Test public void testWriteObject() { for (Object o : new Object[]{10, 9.9, "string", new Date(), BigDecimal.valueOf(1.1)}) { bytes.position(0); @@ -797,7 +898,7 @@ public class NativeBytesTest { int capacity = 16 * 1024; byteBuffer = ByteBuffer.allocateDirect(capacity); long addr = ((DirectBuffer) byteBuffer).address(); - bytes = new NativeBytes(addr, addr, addr + capacity); + bytes = new NativeBytes(addr, addr + capacity); Calendar cal = Calendar.getInstance(); bytes.writeObject(cal); Dummy d = new Dummy(); @@ -833,13 +934,27 @@ public class NativeBytesTest { } } + interface DummyByteable extends Byteable { + void setField1(long field); + + long getField1(); + + void setField2(int field); + + int getField2(); + + void setField3(int field); + + int getField3(); + } + @Test public void testErrors() { int capacity = 1024; byteBuffer = ByteBuffer.allocateDirect(capacity); long addr = ((DirectBuffer) byteBuffer).address(); // it is actually much bigger than it believes - bytes = new NativeBytes(addr, addr, addr + 16); + bytes = new NativeBytes(addr, addr + 16); bytes.writeLong(8); assertFalse(bytes.isFinished()); bytes.finish(); @@ -848,18 +963,14 @@ public class NativeBytesTest { bytes.writeLong(16); bytes.finish(); bytes.flush(); - bytes.writeLong(24); try { + bytes.writeLong(24); bytes.finish(); fail(); } catch (IndexOutOfBoundsException expected) { } - try { - bytes.flush(); - fail(); - } catch (IndexOutOfBoundsException expected) { - } - bytes.reset(); + + bytes.clear(); assertEquals(0, bytes.position()); assertEquals(8, bytes.skip(8)); assertEquals(8, bytes.position()); @@ -871,15 +982,15 @@ public class NativeBytesTest { public void testWriteList() { List<Integer> ints = Arrays.asList(1, 2, 3, 4); bytes.writeList(ints); - bytes.reset(); + bytes.clear(); List<Integer> ints2 = new ArrayList<Integer>(); bytes.readList(ints2, Integer.class); assertEquals(ints, ints2); - bytes.reset(); + bytes.clear(); List<String> words = Arrays.asList("Hello word byte for now".split(" ")); bytes.writeList(words); - bytes.reset(); + bytes.clear(); List<String> words2 = new ArrayList<String>(); bytes.readList(words2, String.class); } @@ -898,7 +1009,7 @@ public class NativeBytesTest { bytes.writeMap(map); bytes.finish(); - bytes.reset(); + bytes.clear(); Map<String, Integer> map2 = new LinkedHashMap<String, Integer>(); bytes.readMap(map2, String.class, Integer.class); assertEquals(map, map2); @@ -910,7 +1021,7 @@ public class NativeBytesTest { ExecutorService es = Executors.newSingleThreadExecutor(new NamedThreadFactory("unloadFailed")); Future<Void> future = es.submit(new Callable<Void>() { @Override - public Void call() throws Exception { + public Void call() { bytes.unlockInt(0); return null; } @@ -923,4 +1034,26 @@ public class NativeBytesTest { assertEquals(IllegalMonitorStateException.class, e.getCause().getClass()); } } + + @Test + public void testToString() { + NativeBytes bytes = new DirectStore(32).bytes(); + assertEquals("[pos: 0, lim: 32, cap: 32 ] ٠٠٠٠٠٠٠٠٠٠٠٠٠٠٠٠٠٠٠٠٠٠٠٠٠٠٠٠٠٠٠٠", bytes.toDebugString()); + bytes.writeByte(1); + assertEquals("[pos: 1, lim: 32, cap: 32 ] ⒈‖٠٠٠٠٠٠٠٠٠٠٠٠٠٠٠٠٠٠٠٠٠٠٠٠٠٠٠٠٠٠٠", bytes.toDebugString()); + bytes.writeByte(2); + assertEquals("[pos: 2, lim: 32, cap: 32 ] ⒈⒉‖٠٠٠٠٠٠٠٠٠٠٠٠٠٠٠٠٠٠٠٠٠٠٠٠٠٠٠٠٠٠", bytes.toDebugString()); + bytes.writeByte(3); + assertEquals("[pos: 3, lim: 32, cap: 32 ] ⒈⒉⒊‖٠٠٠٠٠٠٠٠٠٠٠٠٠٠٠٠٠٠٠٠٠٠٠٠٠٠٠٠٠", bytes.toDebugString()); + bytes.writeByte(4); + assertEquals("[pos: 4, lim: 32, cap: 32 ] ⒈⒉⒊⒋‖٠٠٠٠٠٠٠٠٠٠٠٠٠٠٠٠٠٠٠٠٠٠٠٠٠٠٠٠", bytes.toDebugString()); + bytes.writeByte(5); + assertEquals("[pos: 5, lim: 32, cap: 32 ] ⒈⒉⒊⒋⒌‖٠٠٠٠٠٠٠٠٠٠٠٠٠٠٠٠٠٠٠٠٠٠٠٠٠٠٠", bytes.toDebugString()); + bytes.writeByte(6); + assertEquals("[pos: 6, lim: 32, cap: 32 ] ⒈⒉⒊⒋⒌⒍‖٠٠٠٠٠٠٠٠٠٠٠٠٠٠٠٠٠٠٠٠٠٠٠٠٠٠", bytes.toDebugString()); + bytes.writeByte(7); + assertEquals("[pos: 7, lim: 32, cap: 32 ] ⒈⒉⒊⒋⒌⒍⒎‖٠٠٠٠٠٠٠٠٠٠٠٠٠٠٠٠٠٠٠٠٠٠٠٠٠", bytes.toDebugString()); + bytes.writeByte(8); + assertEquals("[pos: 8, lim: 32, cap: 32 ] ⒈⒉⒊⒋⒌⒍⒎⒏‖٠٠٠٠٠٠٠٠٠٠٠٠٠٠٠٠٠٠٠٠٠٠٠٠", bytes.toDebugString()); + } } diff --git a/lang/src/test/java/net/openhft/lang/io/OptimisedBytesHashTest.java b/lang/src/test/java/net/openhft/lang/io/OptimisedBytesHashTest.java new file mode 100755 index 0000000..6747330 --- /dev/null +++ b/lang/src/test/java/net/openhft/lang/io/OptimisedBytesHashTest.java @@ -0,0 +1,220 @@ +/* + * Copyright (C) 2015 higherfrequencytrading.com + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as published by + * the Free Software Foundation, either version 3 of the License. + * + * 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 Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with this program. If not, see <http://www.gnu.org/licenses/>. + */ + +package net.openhft.lang.io; + +import org.junit.Ignore; +import org.junit.Test; + +import java.security.SecureRandom; +import java.util.Random; + +/** + * Created by peter on 28/06/15. + */ +public class OptimisedBytesHashTest { + + /* + @Test + public void testApplyAsLong() { + Bytes b = DirectStore.allocate(128).bytes(); + b.writeLong(0x0102030405060708L); + b.writeLong(0x1112131415161718L); + b.flip(); + while (b.remaining() > 0) { + assertEquals("Rem: " + b.remaining(), + VanillaBytesStoreHash.INSTANCE.hash(b), + OptimisedBytesHash.INSTANCE.hash(b)); + b.readSkip(1); + } + assertEquals(VanillaBytesStoreHash.INSTANCE.hash(b), + OptimisedBytesHash.INSTANCE.hash(b)); + } +*/ +/* + @Test + public void sizeMatch() { + Bytes nb = DirectStore.allocate(128).bytes(); + for (int i = 1; i <= 64; i++) + nb.writeUnsignedByte(i); + nb.flip(); + assertEquals(0L, applyAsLong1to7(nb, 0)); + for (int i = 1; i <= 7; i++) + assertEquals(applyAsLong1to7(nb, i), applyAsLong9to16(nb, i)); + assertEquals(applyAsLong8(nb), applyAsLong1to7(nb, 8)); + assertEquals(applyAsLong8(nb), applyAsLong9to16(nb, 8)); + for (int i = 1; i <= 16; i++) + assertEquals(applyAsLong9to16(nb, i), applyAsLong17to32(nb, i)); + for (int i = 1; i <= 32; i++) + assertEquals(applyAsLong17to32(nb, i), applyAsLongAny(nb, i)); + } +*/ + @Test + @Ignore("Long running, avg score = 6879") + public void testRandomness() { + long time = 0, timeCount = 0; + long scoreSum = 0; + for (int t = 0; t < 500; t++) { + long[] hashs = new long[8192]; + byte[] init = new byte[hashs.length / 8]; + Bytes b = DirectStore.allocate(init.length).bytes(); + new SecureRandom().nextBytes(init); + for (int i = 0; i < hashs.length; i++) { + b.clear(); + b.write(init); + + b.writeLong(i >> 6 << 3, 1L << i); + + b.position(0); + b.limit(init.length); + long start = System.nanoTime(); + hashs[i] = VanillaBytesHash.INSTANCE.hash(b); + + time += System.nanoTime() - start; + timeCount++; + } + long score = 0; + for (int i = 0; i < hashs.length - 1; i++) + for (int j = i + 1; j < hashs.length; j++) { + long diff = hashs[j] ^ hashs[i]; + int diffBC = Long.bitCount(diff); + if (diffBC <= 17) { + long d = 1L << (17 - diffBC); + score += d; + } + } + scoreSum += score; + if (t % 50 == 0) + System.out.println(t + " - Score: " + score); + } + System.out.println("Average score: " + scoreSum / 500); + System.out.printf("Average time %.3f us%n", time / timeCount / 1e3); + } + + @Test + @Ignore("Long running, avg score = 1594788, note lower is better") + public void testRandomnessOld() { + long time = 0, timeCount = 0; + long scoreSum = 0; + for (int t = 0; t < 500; t++) { + long[] hashs = new long[8192]; + byte[] init = new byte[hashs.length / 8]; + Bytes b = DirectStore.allocate(init.length).bytes(); + new SecureRandom().nextBytes(init); + for (int i = 0; i < hashs.length; i++) { + b.clear(); + b.write(init); + + b.writeLong(i >> 6 << 3, 1L << i); + + b.position(0); + b.limit(init.length); + long start = System.nanoTime(); + hashs[i] = VanillaBytesHasher.INSTANCE.hash(b); + + time += System.nanoTime() - start; + timeCount++; + } + long score = 0; + for (int i = 0; i < hashs.length - 1; i++) + for (int j = i + 1; j < hashs.length; j++) { + long diff = hashs[j] ^ hashs[i]; + int diffBC = Long.bitCount(diff); + if (diffBC <= 17) { + long d = 1L << (17 - diffBC); + score += d; + } + } + scoreSum += score; + if (t % 50 == 0) + System.out.println(t + " - Score: " + score); + } + System.out.println("Average score: " + scoreSum / 500); + System.out.printf("Average time %.3f us%n", time / timeCount / 1e3); + } + + @Test + @Ignore("Long running, avg score = 6823, avg time 0.027 us") + public void testSmallRandomness() { + long time = 0, timeCount = 0; + long scoreSum = 0; +// StringBuilder sb = new StringBuilder(); + + for (int t = 0; t < 500; t++) { + long[] hashs = new long[8192]; + Bytes b = DirectStore.allocate(hashs.length / 64).bytes(); + for (int i = 0; i < hashs.length; i++) { + b.clear(); + b.append(t); + b.append('-'); + b.append(i); + long start = System.nanoTime(); + hashs[i] = VanillaBytesHash.INSTANCE.hash(b); + time += System.nanoTime() - start; + timeCount++; + +/* if (true) { + sb.setLength(0); + sb.append(b); + assertEquals(hashs[i], Maths.longHash(sb)); + }*/ + } + long score = 0; + for (int i = 0; i < hashs.length - 1; i++) + for (int j = i + 1; j < hashs.length; j++) { + long diff = hashs[j] ^ hashs[i]; + int diffBC = Long.bitCount(diff); + if (diffBC < 18) { + long d = 1L << (17 - diffBC); + score += d; + } + } + scoreSum += score; + if (t % 50 == 0) + System.out.println(t + " - Score: " + score); + } + System.out.println("Average score: " + scoreSum / 500); + System.out.printf("Average time %.3f us%n", time / timeCount / 1e3); + } + + @Test + @Ignore("Only run for comparison, avg score = 6843") + public void testSecureRandomness() { + long scoreSum = 0; + for (int t = 0; t < 500; t++) { + Random rand = new SecureRandom(); + long[] hashs = new long[8192]; + for (int i = 0; i < hashs.length; i++) { + hashs[i] = rand.nextLong(); + } + int score = 0; + for (int i = 0; i < hashs.length - 1; i++) + for (int j = i + 1; j < hashs.length; j++) { + long diff = hashs[j] ^ hashs[i]; + int diffBC = Long.bitCount(diff); + if (diffBC < 18) { + int d = 1 << (17 - diffBC); + score += d; + } + } + scoreSum += score; + if (t % 50 == 0) + System.out.println(t + " - Score: " + score); + } + System.out.println("Average score: " + scoreSum / 500); + } + +}
\ No newline at end of file diff --git a/lang/src/test/java/net/openhft/lang/io/PingPongMain.java b/lang/src/test/java/net/openhft/lang/io/PingPongMain.java new file mode 100755 index 0000000..1ebdc5a --- /dev/null +++ b/lang/src/test/java/net/openhft/lang/io/PingPongMain.java @@ -0,0 +1,95 @@ +/* + * Copyright (C) 2015 higherfrequencytrading.com + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as published by + * the Free Software Foundation, either version 3 of the License. + * + * 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 Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with this program. If not, see <http://www.gnu.org/licenses/>. + */ + +package net.openhft.lang.io; + +import java.io.EOFException; +import java.io.IOException; +import java.net.InetSocketAddress; +import java.nio.ByteBuffer; +import java.nio.channels.ServerSocketChannel; +import java.nio.channels.SocketChannel; +import java.util.Arrays; + +/** + * Created by peter.lawrey on 21/11/14. + */ +public class PingPongMain { + public static final int PORT = Integer.getInteger("port", 8181); + public static final int SIZE = Integer.getInteger("size", 128); + + public static void main(String[] args) throws IOException { + if (args.length < 1) + startServer(); + else + startClient(args[0]); + } + + private static void startClient(String hostname) throws IOException { + int runs = 100000; + int[] times = new int[runs]; + SocketChannel sc = SocketChannel.open(new InetSocketAddress(hostname, PORT)); + System.out.println("Connected to " + hostname + ":" + PORT); + sc.configureBlocking(false); + ByteBuffer bb = ByteBuffer.allocateDirect(SIZE); + for (int j = 0; j < 3; j++) { + for (int i = 0; i < runs; i++) { + long start = System.nanoTime(); + bb.clear(); + while (bb.remaining() > 0) { + int len = sc.write(bb); + if (len < 0) + throw new EOFException("write"); + } + bb.clear(); + while (bb.remaining() > 0) { + int len = sc.read(bb); + if (len < 0) + throw new EOFException("read"); + } + times[i] = (int) (System.nanoTime() - start); + } + Arrays.sort(times); + System.out.printf("50/90/99/99.9 %%tile %,d / %,d / %,d / %,d%n", + times[runs / 2] / 1000, + times[runs - runs / 10] / 1000, + times[runs - runs / 100] / 1000, + times[runs - runs / 1000] / 1000 + ); + } + sc.close(); + } + + private static void startServer() throws IOException { + ServerSocketChannel ssc = ServerSocketChannel.open(); + ssc.socket().setReuseAddress(true); + ssc.socket().bind(new InetSocketAddress(PORT)); + + System.out.println("Listening for one connection on port " + PORT); + SocketChannel sc = ssc.accept(); + sc.configureBlocking(false); + ByteBuffer bb = ByteBuffer.allocateDirect(16 * 1024); + bb.clear(); // Prepare buffer for use + while (sc.read(bb) >= 0 || bb.position() != 0) { + bb.flip(); + sc.write(bb); + bb.compact(); // In case of partial write + } + sc.close(); + ssc.close(); + System.out.println("... finished"); + } +} diff --git a/lang/src/test/java/net/openhft/lang/io/ResizeableMappedStoreTest.java b/lang/src/test/java/net/openhft/lang/io/ResizeableMappedStoreTest.java new file mode 100644 index 0000000..45b18f7 --- /dev/null +++ b/lang/src/test/java/net/openhft/lang/io/ResizeableMappedStoreTest.java @@ -0,0 +1,70 @@ +/* + * Copyright (C) 2015 higherfrequencytrading.com + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as published by + * the Free Software Foundation, either version 3 of the License. + * + * 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 Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with this program. If not, see <http://www.gnu.org/licenses/>. + */ +package net.openhft.lang.io; + +import static org.junit.Assert.assertEquals; + +import java.io.File; +import java.io.IOException; +import java.nio.channels.FileChannel; + +import org.junit.After; +import org.junit.Test; + +public final class ResizeableMappedStoreTest { + @After + public void tearDown() { + System.gc(); + } + + @Test + public void testResizableMappedStore() throws IOException { + File file = MappedStoreTest.getStoreFile("resizable-mapped-store.tmp"); + + final int smallSize = 1024, largeSize = 10 * smallSize; + + { + ResizeableMappedStore ms = new ResizeableMappedStore(file, FileChannel.MapMode.READ_WRITE, smallSize); + + DirectBytes slice1 = ms.bytes(); + for (int i = 0; i < smallSize; ++i) { + slice1.writeByte(42); + } + + ms.resize(largeSize); + + DirectBytes slice2 = ms.bytes(); + slice2.skipBytes(smallSize); + for (int i = smallSize; i < largeSize; ++i) { + slice2.writeByte(24); + } + + ms.close(); + } + + assertEquals(largeSize, file.length()); + + { + ResizeableMappedStore ms = new ResizeableMappedStore(file, FileChannel.MapMode.READ_WRITE, file.length()); + DirectBytes slice = ms.bytes(); + assertEquals(42, slice.readByte(smallSize - 1)); + assertEquals(24, slice.readByte(largeSize - 1)); + slice.release(); + + ms.close(); + } + } +} diff --git a/lang/src/test/java/net/openhft/lang/io/VanillaMappedFileTest.java b/lang/src/test/java/net/openhft/lang/io/VanillaMappedFileTest.java new file mode 100755 index 0000000..ca5087f --- /dev/null +++ b/lang/src/test/java/net/openhft/lang/io/VanillaMappedFileTest.java @@ -0,0 +1,376 @@ +/* + * Copyright (C) 2015 higherfrequencytrading.com + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as published by + * the Free Software Foundation, either version 3 of the License. + * + * 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 Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with this program. If not, see <http://www.gnu.org/licenses/>. + */ +package net.openhft.lang.io; + +import org.junit.After; +import org.junit.Before; +import org.junit.Test; + +import java.io.File; +import java.io.IOException; +import java.io.Serializable; + +import static org.junit.Assert.*; + +public class VanillaMappedFileTest { + public static String TMPDIR = System.getProperty("java.io.tmpdir"); + public static String SEPARATOR = System.getProperty("file.separator"); + public static String BASEPATH = TMPDIR + SEPARATOR + "vmf"; + + private static File newTempraryFile(String name) { + return newTempraryFile(name, true); + } + + private static File newTempraryFile(String name, boolean delete) { + File file = new File( + BASEPATH, + name); + + if (delete) { + file.delete(); + file.deleteOnExit(); + } + + if(!file.getParentFile().exists()) { + file.getParentFile().mkdirs(); + } + + return file; + } + + // ************************************************************************* + // + // ************************************************************************* + + @Before + public void setUp() { + } + + @After + public void tearDown() { + } + + // ************************************************************************* + // + // ************************************************************************* + + @Test + public void testCreate() throws IOException { + File f1 = newTempraryFile("vmf-create-1"); + File f2 = newTempraryFile("vmf-create-2"); + + VanillaMappedFile vmf1 = VanillaMappedFile.readWrite(f1); + VanillaMappedFile vmf2 = VanillaMappedFile.readWrite(f2,128); + + assertTrue(f1.exists()); + assertTrue(f2.exists()); + + assertEquals(0, vmf1.size()); + assertEquals(128, vmf2.size()); + + vmf1.close(); + vmf2.close(); + } + + @Test + public void testAcquireBytes() throws IOException { + VanillaMappedFile vmf = VanillaMappedFile.readWrite( + newTempraryFile("vmf-acquire-buffer")); + + assertTrue(new File(vmf.path()).exists()); + + VanillaMappedBytes buffer = vmf.bytes(0,128); + assertEquals(1, buffer.refCount()); + assertEquals(0L, buffer.readLong(0L)); + assertEquals(0L, buffer.readLong(1L)); + + buffer.writeLong(0, 1L); + assertEquals(1L, buffer.readLong(0)); + assertEquals(128, buffer.size()); + + buffer.writeLong(2L); + assertEquals(2L, buffer.readLong(0)); + assertEquals(120, buffer.remaining()); + + buffer.release(); + vmf.close(); + } + + @Test + public void testAcquireBlocks1() throws IOException { + VanillaMappedBlocks blocks = VanillaMappedBlocks.readWrite( + newTempraryFile("vmf-acquire-blocks-1"), + 128); + + assertTrue(new File(blocks.path()).exists()); + + VanillaMappedBytes b1 = blocks.acquire(0); + assertEquals(128, blocks.size()); + assertEquals(2, b1.refCount()); + assertEquals(128, b1.size()); + + VanillaMappedBytes b2 = blocks.acquire(1); + assertEquals(256, blocks.size()); + assertEquals(2, b2.refCount()); + assertEquals(128, b2.size()); + + VanillaMappedBytes b3 = blocks.acquire(1); + assertEquals(256, blocks.size()); + assertEquals(3, b3.refCount()); + assertEquals(128, b2.size()); + + assertNotEquals(b1.address(), b2.address()); + assertNotEquals(b1.address(), b3.address()); + assertEquals(b2.address(), b3.address()); + + b1.release(); + b2.release(); + b2.release(); + + blocks.close(); + } + + @Test + public void testAcquireBlocks2() throws IOException { + VanillaMappedBlocks blocks = VanillaMappedBlocks.readWrite( + newTempraryFile("vmf-acquire-blocks-2"), + 64); + + assertTrue(new File(blocks.path()).exists()); + + final long nblocks = 10000; + for (long i = 0; i < nblocks; i++) { + VanillaMappedBytes b = blocks.acquire(i); + assertEquals(2, b.refCount()); + + b.release(); + + assertEquals(1, b.refCount()); + assertFalse(b.unmapped()); + } + + blocks.close(); + } + + @Test + public void testAcquireOverlap() throws IOException { + File path = newTempraryFile("vmf-acquire-overlap"); + + VanillaMappedFile vmf = VanillaMappedFile.readWrite(path); + VanillaMappedBlocks blocks = VanillaMappedBlocks.readWrite(path,128); + + VanillaMappedBytes b1 = blocks.acquire(0); + b1.writeLong(1); + b1.release(); + + assertEquals(1, b1.refCount()); + assertFalse(b1.unmapped()); + + VanillaMappedBytes b2 = blocks.acquire(1); + b2.writeLong(2); + b2.release(); + + assertEquals(1, b2.refCount()); + assertFalse(b1.unmapped()); + assertFalse(b2.unmapped()); + + VanillaMappedBytes b3 = blocks.acquire(2); + b3.writeLong(3); + b3.release(); + + assertEquals(1, b3.refCount()); + assertTrue(b1.unmapped()); + assertFalse(b2.unmapped()); + assertFalse(b3.unmapped()); + + VanillaMappedBytes b4 = vmf.bytes(0, 128 * 3); + assertEquals( 1, b4.refCount()); + assertEquals(384, b4.size()); + assertEquals( 1L, b4.readLong(0)); + assertEquals( 2L, b4.readLong(128)); + assertEquals( 3L, b4.readLong(256)); + + vmf.close(); + blocks.close(); + } + + @Test + public void testReopen() throws IOException { + File file = newTempraryFile("vmf-reopen"); + + { + VanillaMappedFile vmf = VanillaMappedFile.readWrite(file); + VanillaMappedBytes buf = vmf.bytes(0,128); + + buf.writeLong(0, 1L); + + assertEquals(1L , buf.readLong(0)); + assertEquals(128, vmf.size()); + + buf.release(); + vmf.close(); + } + + { + VanillaMappedFile vmf = VanillaMappedFile.readWrite(file); + VanillaMappedBytes buf = vmf.bytes(0,128); + + assertEquals(1L , buf.readLong(0)); + assertEquals(128, vmf.size()); + + buf.release(); + vmf.close(); + } + + assertEquals(128, file.length()); + } + + // ************************************************************************* + // + // ************************************************************************* + + @Test + public void testMappedCache1() throws IOException { + VanillaMappedCache<Integer> cache = new VanillaMappedCache(); + + assertEquals(cache.size(),0); + assertNull(cache.get(1)); + + cache.put(1, newTempraryFile("vmc-1-v1"), 64); + cache.put(2, newTempraryFile("vmc-1-v2"), 128); + + assertEquals(cache.size(),2); + + assertNotNull(cache.get(1)); + assertNotNull(cache.get(2)); + + VanillaMappedBytes b1 = cache.get(1); + assertEquals( 1, b1.refCount()); + assertEquals( 64, b1.size()); + + VanillaMappedBytes b2 = cache.get(2); + assertEquals( 1, b2.refCount()); + assertEquals(128, b2.size()); + + cache.close(); + } + + @Test + public void testMappedCache2() throws IOException { + final int size = 5; + VanillaMappedCache<Integer> cache = new VanillaMappedCache(size, true); + for(int i=0;i<10;i++) { + cache.put(i, newTempraryFile("vmc-2-v" + i), 8 * i, i); + if(i >= size) { + assertEquals(cache.size(), size - 1); + } + } + + for(int i=10-1;i>=0;i--) { + if(i >= 6) { + assertNotNull(cache.get(i)); + assertEquals(cache.get(i).index(),i); + + } else { + assertNull(cache.get(i)); + } + } + + cache.close(); + } + + @Test + public void testMappedCache3() throws IOException { + VanillaMappedCache<Integer> cache = new VanillaMappedCache(32, false); + VanillaMappedBytes buffer = null; + File file = null; + + for (int j = 0; j < 5; j++) { + long start = System.nanoTime(); + int maxRuns = 10000, runs; + for (runs = 0; runs < maxRuns; runs++) { + file = newTempraryFile("vmc-3-v" + runs, false); + + buffer = cache.put(runs, file, 256, runs); + buffer.writeLong(0, 0x12345678); + + assertEquals(0x12345678L, buffer.readLong(0)); + assertEquals(runs, buffer.index()); + + buffer.release(); + buffer.close(); + + assertEquals(0, buffer.refCount()); + assertTrue(file.delete()); + if (System.nanoTime() - start > 1e9) + break; + } + + long time = System.nanoTime() - start; + System.out.printf("The average time was %,d us%n", time / runs / 1000); + } + + cache.close(); + } + + @Test + public void testMappedCache4() throws IOException { + VanillaMappedCache<Integer> cache = new VanillaMappedCache(10000, true); + VanillaMappedBytes buffer = cache.put(1, newTempraryFile("vmc-4"), 256, 1); + + buffer.reserve(); + assertEquals(2,buffer.refCount()); + + buffer.release(); + assertEquals(1,buffer.refCount()); + + cache.close(); + assertEquals(0,buffer.refCount()); + } + + // ************************************************************************* + // + // ************************************************************************* + + @Test + public void testMessageKeySerialization() throws IOException { + File path = newTempraryFile("vmc-x"); + VanillaMappedBytes bytes = VanillaMappedFile.readWriteBytes(path, 1024, 0, + FileLifecycleListener.FileLifecycleListeners.CONSOLE); + + bytes.writeObject(new MessageKey("type", 123L)); + bytes.flush(); + + bytes.position(0); + System.out.println("" + bytes.readObject(MessageKey.class)); + + bytes.close(); + } + + public static class MessageKey implements Serializable { + private String arg1; + private long arg2; + public MessageKey(String arg1, long arg2) { + this.arg1 = arg1; + this.arg2 = arg2; + } + @Override + public String toString() { + return "MessageKey{" + "arg1='" + arg1 + ", arg2=" + arg2 + "}"; + } + } +} + diff --git a/lang/src/test/java/net/openhft/lang/io/examples/MappedStroreExampleMain.java b/lang/src/test/java/net/openhft/lang/io/examples/MappedStroreExampleMain.java new file mode 100755 index 0000000..f083dd7 --- /dev/null +++ b/lang/src/test/java/net/openhft/lang/io/examples/MappedStroreExampleMain.java @@ -0,0 +1,44 @@ +/* + * Copyright (C) 2015 higherfrequencytrading.com + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as published by + * the Free Software Foundation, either version 3 of the License. + * + * 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 Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with this program. If not, see <http://www.gnu.org/licenses/>. + */ + +package net.openhft.lang.io.examples; + +import net.openhft.lang.io.DirectBytes; +import net.openhft.lang.io.MappedStore; + +import java.io.File; +import java.io.IOException; +import java.nio.channels.FileChannel; + +/** + * Created by peter.lawrey on 14/07/14. + */ +public class MappedStroreExampleMain { + public static void main(String[] args) throws IOException { + File deleteme = File.createTempFile("deleteme", ".tmp"); + deleteme.deleteOnExit(); + // 4 GB of memory. + long size = 4L << 30; + long start = System.currentTimeMillis(); + MappedStore ms = new MappedStore(deleteme, FileChannel.MapMode.READ_WRITE, size); + DirectBytes bytes = ms.bytes(); + for(long i = 0; i < size; i+= 4) + bytes.writeLong(i); + ms.free(); + long time = System.currentTimeMillis() - start; + System.out.printf("Wrote %,d MB/s%n", size / 1000 / time); + } +} diff --git a/lang/src/test/java/net/openhft/lang/io/examples/ParserExampleMain.java b/lang/src/test/java/net/openhft/lang/io/examples/ParserExampleMain.java index c9ec1b5..53ca541 100644..100755 --- a/lang/src/test/java/net/openhft/lang/io/examples/ParserExampleMain.java +++ b/lang/src/test/java/net/openhft/lang/io/examples/ParserExampleMain.java @@ -1,22 +1,23 @@ /* - * Copyright 2013 Peter Lawrey + * Copyright (C) 2015 higherfrequencytrading.com * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as published by + * the Free Software Foundation, either version 3 of the License. * - * http://www.apache.org/licenses/LICENSE-2.0 + * 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 Lesser General Public License for more details. * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. + * You should have received a copy of the GNU Lesser General Public License + * along with this program. If not, see <http://www.gnu.org/licenses/>. */ package net.openhft.lang.io.examples; import net.openhft.lang.io.ByteBufferBytes; +import net.openhft.lang.io.Bytes; import net.openhft.lang.io.StopCharTesters; import java.nio.ByteBuffer; @@ -25,24 +26,24 @@ import static org.junit.Assert.assertEquals; /** * Run with -verbosegc -Xmx32m - * <p/> - * Average time was 282 nano-seconds + * + * <p>Average time was 282 nano-seconds */ public class ParserExampleMain { public static void main(String... ignored) { ByteBuffer wrap = ByteBuffer.allocate(1024); - ByteBufferBytes bufferBytes = new ByteBufferBytes(wrap); + Bytes bufferBytes = ByteBufferBytes.wrap(wrap); byte[] bytes = "BAC,12.32,12.54,12.56,232443".getBytes(); int runs = 10000000; long start = System.nanoTime(); for (int i = 0; i < runs; i++) { - bufferBytes.reset(); + bufferBytes.clear(); // read the next message. bufferBytes.write(bytes); bufferBytes.position(0); // decode message - String word = bufferBytes.parseUTF(StopCharTesters.COMMA_STOP); + String word = bufferBytes.parseUtf8(StopCharTesters.COMMA_STOP); double low = bufferBytes.parseDouble(); double curr = bufferBytes.parseDouble(); double high = bufferBytes.parseDouble(); diff --git a/lang/src/test/java/net/openhft/lang/io/serialization/ByteMarshallableMarshallerTest.java b/lang/src/test/java/net/openhft/lang/io/serialization/ByteMarshallableMarshallerTest.java index 95df903..1db20f9 100644 --- a/lang/src/test/java/net/openhft/lang/io/serialization/ByteMarshallableMarshallerTest.java +++ b/lang/src/test/java/net/openhft/lang/io/serialization/ByteMarshallableMarshallerTest.java @@ -1,24 +1,24 @@ /* - * Copyright 2013 Peter Lawrey + * Copyright (C) 2015 higherfrequencytrading.com * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as published by + * the Free Software Foundation, either version 3 of the License. * - * http://www.apache.org/licenses/LICENSE-2.0 + * 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 Lesser General Public License for more details. * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. + * You should have received a copy of the GNU Lesser General Public License + * along with this program. If not, see <http://www.gnu.org/licenses/>. */ package net.openhft.lang.io.serialization; import net.openhft.lang.io.Bytes; import net.openhft.lang.io.NativeBytes; -import org.jetbrains.annotations.NotNull; +import net.openhft.lang.model.constraints.NotNull; import org.junit.Test; import sun.nio.ch.DirectBuffer; @@ -37,12 +37,12 @@ public class ByteMarshallableMarshallerTest { int capacity = 2 * 1024; ByteBuffer byteBuffer = ByteBuffer.allocateDirect(capacity); long addr = ((DirectBuffer) byteBuffer).address(); - NativeBytes nativeBytes = new NativeBytes(addr, addr, addr + capacity); + NativeBytes nativeBytes = new NativeBytes(addr, addr + capacity); BytesMarshallable bm = new MockBytesMarshallable(12345678); nativeBytes.writeObject(bm); nativeBytes.finish(); - nativeBytes.reset(); + nativeBytes.clear(); BytesMarshallable bm2 = nativeBytes.readObject(MockBytesMarshallable.class); assertEquals(bm, bm2); } @@ -71,9 +71,8 @@ public class ByteMarshallableMarshallerTest { MockBytesMarshallable that = (MockBytesMarshallable) o; - if (number != that.number) return false; + return number == that.number; - return true; } } } diff --git a/lang/src/test/java/net/openhft/lang/io/serialization/ExternalizableMarshallerTest.java b/lang/src/test/java/net/openhft/lang/io/serialization/ExternalizableMarshallerTest.java index 70bad3e..6e665b0 100644 --- a/lang/src/test/java/net/openhft/lang/io/serialization/ExternalizableMarshallerTest.java +++ b/lang/src/test/java/net/openhft/lang/io/serialization/ExternalizableMarshallerTest.java @@ -1,17 +1,17 @@ /* - * Copyright 2013 Peter Lawrey + * Copyright (C) 2015 higherfrequencytrading.com * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as published by + * the Free Software Foundation, either version 3 of the License. * - * http://www.apache.org/licenses/LICENSE-2.0 + * 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 Lesser General Public License for more details. * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. + * You should have received a copy of the GNU Lesser General Public License + * along with this program. If not, see <http://www.gnu.org/licenses/>. */ package net.openhft.lang.io.serialization; @@ -39,12 +39,12 @@ public class ExternalizableMarshallerTest { int capacity = 2 * 1024; ByteBuffer byteBuffer = ByteBuffer.allocateDirect(capacity); long addr = ((DirectBuffer) byteBuffer).address(); - NativeBytes nativeBytes = new NativeBytes(addr, addr, addr + capacity); + NativeBytes nativeBytes = new NativeBytes(addr, addr + capacity); Externalizable bm = new MockExternalizable(12345678); nativeBytes.writeObject(bm); nativeBytes.finish(); - nativeBytes.reset(); + nativeBytes.clear(); Externalizable bm2 = nativeBytes.readObject(MockExternalizable.class); assertEquals(bm, bm2); } @@ -73,9 +73,8 @@ public class ExternalizableMarshallerTest { MockExternalizable that = (MockExternalizable) o; - if (number != that.number) return false; + return number == that.number; - return true; } } } diff --git a/lang/src/test/java/net/openhft/lang/io/serialization/JDKZObjectSerializerTest.java b/lang/src/test/java/net/openhft/lang/io/serialization/JDKZObjectSerializerTest.java new file mode 100644 index 0000000..73f5d29 --- /dev/null +++ b/lang/src/test/java/net/openhft/lang/io/serialization/JDKZObjectSerializerTest.java @@ -0,0 +1,58 @@ +/* + * Copyright (C) 2015 higherfrequencytrading.com + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as published by + * the Free Software Foundation, either version 3 of the License. + * + * 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 Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with this program. If not, see <http://www.gnu.org/licenses/>. + */ + +package net.openhft.lang.io.serialization; + +import net.openhft.lang.io.DirectBytes; +import net.openhft.lang.io.DirectStore; +import org.junit.Test; + +import java.io.IOException; +import java.io.ObjectOutputStream; +import java.util.zip.DeflaterOutputStream; + +import static org.junit.Assert.assertEquals; + +public class JDKZObjectSerializerTest { + + @Test + public void testReadSerializable() throws IOException, ClassNotFoundException { + { + DirectBytes bytes = DirectStore.allocate(1024).bytes(); + bytes.writeInt(0); + ObjectOutputStream oos = new ObjectOutputStream(bytes.outputStream()); + oos.writeObject("hello"); + oos.close(); + bytes.writeUnsignedInt(0, bytes.position() - 4); + + bytes.flip(); + assertEquals("hello", JDKZObjectSerializer.INSTANCE.readSerializable(bytes, null, null)); + bytes.release(); + } + { + DirectBytes bytes = DirectStore.allocate(1024).bytes(); + bytes.writeInt(0); + ObjectOutputStream oos = new ObjectOutputStream(new DeflaterOutputStream(bytes.outputStream())); + oos.writeObject("hello world"); + oos.close(); + bytes.writeUnsignedInt(0, bytes.position() - 4); + + bytes.flip(); + assertEquals("hello world", JDKZObjectSerializer.INSTANCE.readSerializable(bytes, null, null)); + bytes.close(); + } + } +}
\ No newline at end of file diff --git a/lang/src/test/java/net/openhft/lang/io/serialization/JavaSerializationTest.java b/lang/src/test/java/net/openhft/lang/io/serialization/JavaSerializationTest.java new file mode 100644 index 0000000..d15fb51 --- /dev/null +++ b/lang/src/test/java/net/openhft/lang/io/serialization/JavaSerializationTest.java @@ -0,0 +1,47 @@ +/* + * Copyright (C) 2015 higherfrequencytrading.com + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as published by + * the Free Software Foundation, either version 3 of the License. + * + * 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 Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with this program. If not, see <http://www.gnu.org/licenses/>. + */ + +package net.openhft.lang.io.serialization; + +import net.openhft.lang.io.ByteBufferBytes; +import net.openhft.lang.io.Bytes; +import org.junit.Assert; +import org.junit.Ignore; +import org.junit.Test; + +import java.nio.ByteBuffer; +import java.nio.ByteOrder; + +/** + * @author Rob Austin. + */ +public class JavaSerializationTest { + + @Test + @Ignore + public void NullPointerException() { + final Bytes bytes = ByteBufferBytes.wrap(ByteBuffer.allocate(1024).order + (ByteOrder.nativeOrder())); + + NullPointerException expected = new NullPointerException("test"); + bytes.writeObject(expected); + + bytes.position(0); + NullPointerException actual = (NullPointerException)bytes.readObject(); + + Assert.assertEquals(expected, actual); + } +} diff --git a/lang/src/test/java/net/openhft/lang/io/serialization/VanillaBytesMarshallerTest.java b/lang/src/test/java/net/openhft/lang/io/serialization/VanillaBytesMarshallerTest.java index b258e32..f6cb1a7 100644 --- a/lang/src/test/java/net/openhft/lang/io/serialization/VanillaBytesMarshallerTest.java +++ b/lang/src/test/java/net/openhft/lang/io/serialization/VanillaBytesMarshallerTest.java @@ -1,17 +1,17 @@ /* - * Copyright 2013 Peter Lawrey + * Copyright (C) 2015 higherfrequencytrading.com * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as published by + * the Free Software Foundation, either version 3 of the License. * - * http://www.apache.org/licenses/LICENSE-2.0 + * 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 Lesser General Public License for more details. * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. + * You should have received a copy of the GNU Lesser General Public License + * along with this program. If not, see <http://www.gnu.org/licenses/>. */ package net.openhft.lang.io.serialization; @@ -19,27 +19,36 @@ package net.openhft.lang.io.serialization; import net.openhft.lang.io.DirectBytes; import net.openhft.lang.io.DirectStore; import net.openhft.lang.io.NativeBytes; +import net.openhft.lang.io.serialization.impl.VanillaBytesMarshallerFactory; import org.junit.Test; import sun.nio.ch.DirectBuffer; +import java.io.EOFException; +import java.io.IOException; import java.nio.ByteBuffer; +import java.util.concurrent.atomic.AtomicInteger; -import static org.junit.Assert.assertEquals; +import static org.junit.Assert.*; /** * User: peter.lawrey Date: 20/09/13 Time: 09:28 */ public class VanillaBytesMarshallerTest { + + enum BuySell { + BUY, SELL + } + @Test public void testObjects() { - DirectBytes bytes = new DirectStore(1024).createSlice(); + DirectBytes bytes = new DirectStore(1024).bytes(); Object[] objects = {1, 1L, 1.0, "Hello"}; for (Object o : objects) { long pos = bytes.position(); bytes.writeObject(o); System.out.printf("%s used %,d bytes%n", o.getClass(), bytes.position() - pos); } - bytes.reset(); + bytes.clear(); for (Object o : objects) { Object o2 = bytes.readObject(); assertEquals(o, o2); @@ -51,17 +60,62 @@ public class VanillaBytesMarshallerTest { int capacity = 2 * 1024; ByteBuffer byteBuffer = ByteBuffer.allocateDirect(capacity); long addr = ((DirectBuffer) byteBuffer).address(); - NativeBytes nativeBytes = new NativeBytes(addr, addr, addr + capacity); + NativeBytes nativeBytes = new NativeBytes(addr, addr + capacity); nativeBytes.writeObject(BuySell.BUY); nativeBytes.writeObject(BuySell.SELL); nativeBytes.finish(); - nativeBytes.reset(); + nativeBytes.clear(); assertEquals(BuySell.BUY, nativeBytes.readObject()); assertEquals(BuySell.SELL, nativeBytes.readObject()); } - enum BuySell { - BUY, SELL + @Test + public void testExceptionWithoutCause() { + final int capacity = 2 * 1024; + final ByteBuffer byteBuffer = ByteBuffer.allocateDirect(capacity); + final long addr = ((DirectBuffer) byteBuffer).address(); + + final NativeBytes nativeBytes = new NativeBytes( + new VanillaBytesMarshallerFactory(), addr, addr + capacity, new AtomicInteger(1)); + + Throwable expected = new IOException("io-exception"); + + nativeBytes.writeObject(expected); + nativeBytes.finish(); + nativeBytes.clear(); + + Throwable actual = nativeBytes.readObject(Throwable.class); + assertNotNull(actual); + assertNull(actual.getCause()); + + assertEquals(expected.getMessage(), actual.getMessage()); + assertArrayEquals(expected.getStackTrace(), actual.getStackTrace()); + } + + @Test + public void testExceptionWithCause() { + final int capacity = 2 * 1024; + final ByteBuffer byteBuffer = ByteBuffer.allocateDirect(capacity); + final long addr = ((DirectBuffer) byteBuffer).address(); + + final NativeBytes nativeBytes = new NativeBytes( + new VanillaBytesMarshallerFactory(), addr, addr + capacity, new AtomicInteger(1)); + + Throwable expected = new IOException( + "io-exception", new EOFException("eof-exception")); + + nativeBytes.writeObject(expected); + nativeBytes.finish(); + nativeBytes.clear(); + + Throwable actual = nativeBytes.readObject(Throwable.class); + assertNotNull(actual); + assertNotNull(actual.getCause()); + + assertEquals(expected.getMessage(), actual.getMessage()); + assertEquals(expected.getCause().getMessage(), actual.getCause().getMessage()); + assertArrayEquals(expected.getStackTrace(), actual.getStackTrace()); + assertArrayEquals(expected.getCause().getStackTrace(), actual.getCause().getStackTrace()); } } diff --git a/lang/src/test/java/net/openhft/lang/io/serialization/impl/SnappyStringMarshallerTest.java b/lang/src/test/java/net/openhft/lang/io/serialization/impl/SnappyStringMarshallerTest.java new file mode 100644 index 0000000..f6c68b7 --- /dev/null +++ b/lang/src/test/java/net/openhft/lang/io/serialization/impl/SnappyStringMarshallerTest.java @@ -0,0 +1,53 @@ +/* + * Copyright (C) 2015 higherfrequencytrading.com + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as published by + * the Free Software Foundation, either version 3 of the License. + * + * 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 Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with this program. If not, see <http://www.gnu.org/licenses/>. + */ + +package net.openhft.lang.io.serialization.impl; + +import net.openhft.lang.io.Bytes; +import net.openhft.lang.io.DirectStore; +import org.junit.Test; + +import java.nio.charset.Charset; +import java.util.Random; + +import static org.junit.Assert.assertEquals; + +public class SnappyStringMarshallerTest { + + @Test + public void testWriteRead() { + Bytes b = DirectStore.allocate(64 * 1024).bytes(); + testWriteRead(b, ""); + testWriteRead(b, null); + testWriteRead(b, "Hello World"); + testWriteRead(b, new String(new char[1000000])); + byte[] bytes = new byte[64000]; + Random random = new Random(); + for (int i = 0; i < bytes.length; i++) + bytes[i] = (byte) ('A' + random.nextInt(26)); + testWriteRead(b, new String(bytes, Charset.forName("ISO-8859-1"))); + } + + private void testWriteRead(Bytes b, String s) { + b.clear(); + SnappyStringMarshaller.INSTANCE.write(b, s); + b.writeInt(0x12345678); + b.flip(); + String s2 = SnappyStringMarshaller.INSTANCE.read(b); + assertEquals(0x12345678, b.readInt()); + assertEquals(s, s2); + } +}
\ No newline at end of file diff --git a/lang/src/test/java/net/openhft/lang/io/serialization/impl/StringZMapMarshallerTest.java b/lang/src/test/java/net/openhft/lang/io/serialization/impl/StringZMapMarshallerTest.java new file mode 100644 index 0000000..a80b88a --- /dev/null +++ b/lang/src/test/java/net/openhft/lang/io/serialization/impl/StringZMapMarshallerTest.java @@ -0,0 +1,59 @@ +/* + * Copyright (C) 2015 higherfrequencytrading.com + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as published by + * the Free Software Foundation, either version 3 of the License. + * + * 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 Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with this program. If not, see <http://www.gnu.org/licenses/>. + */ + +package net.openhft.lang.io.serialization.impl; + +import net.openhft.lang.io.Bytes; +import net.openhft.lang.io.DirectStore; +import org.junit.Test; + +import java.util.Collections; +import java.util.LinkedHashMap; +import java.util.Map; + +import static org.junit.Assert.assertEquals; + +public class StringZMapMarshallerTest { + static final Bytes b = DirectStore.allocate(1024).bytes(); + + @Test + public void testWriteRead() { + testWriteRead(null); + testWriteRead(Collections.<String, String>emptyMap()); + testWriteRead(mapOf("Hello", "World", "aye", "alpha", "bee", "beta", "zed", "zeta")); + } + + private void testWriteRead(Map<String, String> map) { + b.clear(); + StringZMapMarshaller.INSTANCE.write(b, map); + b.writeInt(0x12345678); + b.flip(); + Map<String, String> s2 = StringZMapMarshaller.INSTANCE.read(b); + assertEquals(map, s2); + assertEquals(0x12345678, b.readInt()); + } + + public static <K, V> Map<K, V> mapOf(K k, V v, Object... keysAndValues) { + Map<K, V> ret = new LinkedHashMap<K, V>(); + ret.put(k, v); + for (int i = 0; i < keysAndValues.length - 1; i += 2) { + Object key = keysAndValues[i]; + Object value = keysAndValues[i + 1]; + ret.put((K) key, (V) value); + } + return ret; + } +}
\ No newline at end of file diff --git a/lang/src/test/java/net/openhft/lang/locks/LockingStrategyTest.java b/lang/src/test/java/net/openhft/lang/locks/LockingStrategyTest.java new file mode 100644 index 0000000..66537b7 --- /dev/null +++ b/lang/src/test/java/net/openhft/lang/locks/LockingStrategyTest.java @@ -0,0 +1,537 @@ +/* + * Copyright (C) 2015 higherfrequencytrading.com + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as published by + * the Free Software Foundation, either version 3 of the License. + * + * 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 Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with this program. If not, see <http://www.gnu.org/licenses/>. + */ + +package net.openhft.lang.locks; + +import net.openhft.lang.io.ByteBufferBytes; +import net.openhft.lang.io.Bytes; +import org.junit.After; +import org.junit.Before; +import org.junit.Test; +import org.junit.runner.RunWith; +import org.junit.runners.Parameterized; +import sun.nio.ch.DirectBuffer; + +import java.nio.ByteBuffer; +import java.util.Collection; +import java.util.concurrent.*; + +import static java.util.Arrays.asList; +import static net.openhft.lang.locks.LockingStrategyTest.AccessMethod.ADDRESS; +import static net.openhft.lang.locks.LockingStrategyTest.AccessMethod.BYTES_WITH_OFFSET; +import static org.junit.Assert.*; +import static org.junit.Assume.assumeTrue; + +@RunWith(value = Parameterized.class) +public class LockingStrategyTest { + + enum AccessMethod {ADDRESS, BYTES_WITH_OFFSET} + + @Parameterized.Parameters + public static Collection<Object[]> data() { + return asList(new Object[][] { + {VanillaReadWriteUpdateWithWaitsLockingStrategy.instance(), ADDRESS}, + {VanillaReadWriteUpdateWithWaitsLockingStrategy.instance(), BYTES_WITH_OFFSET}, + {VanillaReadWriteWithWaitsLockingStrategy.instance(), ADDRESS}, + {VanillaReadWriteWithWaitsLockingStrategy.instance(), BYTES_WITH_OFFSET}, + }); + } + + ExecutorService e1, e2; + ByteBuffer buffer; + Bytes bytes; + long offset; + LockingStrategy lockingStrategy; + AccessMethod accessMethod; + NativeAtomicAccess access; + Object accessObj; + + public LockingStrategyTest(LockingStrategy lockingStrategy, AccessMethod accessMethod) { + this.lockingStrategy = lockingStrategy; + this.accessMethod = accessMethod; + } + + @Before + public void setUp() { + e1 = new ThreadPoolExecutor(0, 1, Integer.MAX_VALUE, TimeUnit.SECONDS, + new LinkedBlockingQueue<Runnable>()); + e2 = new ThreadPoolExecutor(0, 1, Integer.MAX_VALUE, TimeUnit.SECONDS, + new LinkedBlockingQueue<Runnable>()); + + buffer = ByteBuffer.allocateDirect(8); + bytes = new ByteBufferBytes(buffer); + offset = accessMethod == ADDRESS ? ((DirectBuffer) buffer).address() : 0L; + accessObj = accessMethod == BYTES_WITH_OFFSET ? bytes : null; + access = accessMethod == ADDRESS ? NativeAtomicAccess.unsafe() : + NativeAtomicAccess.toBytes(); + rwls().reset(); + } + + @After + public void tearDown() { + e1.shutdown(); + e2.shutdown(); + } + + Callable<Boolean> tryReadLockTask = new Callable<Boolean>() { + @Override + public Boolean call() { + return rwls().tryReadLock(); + } + }; + + class TestReadWriteLockState extends AbstractReadWriteLockState { + + private ReadWriteLockingStrategy rwls() { + return (ReadWriteLockingStrategy) lockingStrategy; + } + + @Override + public boolean tryReadLock() { + return rwls().tryReadLock(access, accessObj, offset); + } + + @Override + public boolean tryWriteLock() { + return rwls().tryWriteLock(access, accessObj, offset); + } + + @Override + public boolean tryUpgradeReadToWriteLock() { + return rwls().tryUpgradeReadToWriteLock(access, accessObj, offset); + } + + @Override + public void readUnlock() { + rwls().readUnlock(access, accessObj, offset); + } + + @Override + public void writeUnlock() { + rwls().writeUnlock(access, accessObj, offset); + } + + @Override + public void downgradeWriteToReadLock() { + rwls().downgradeWriteToReadLock(access, accessObj, offset); + } + + @Override + public void reset() { + rwls().reset(access, accessObj, offset); + } + + @Override + public long getState() { + return rwls().getState(access, accessObj, offset); + } + + @Override + public ReadWriteLockingStrategy lockingStrategy() { + return rwls(); + } + } + TestReadWriteLockState rwLockState = new TestReadWriteLockState(); + + ReadWriteLockState rwls() { + return rwLockState; + } + + class TestReadWriteUpdateLockState extends TestReadWriteLockState + implements ReadWriteUpdateLockState { + + ReadWriteUpdateLockingStrategy rwuls() { + return (ReadWriteUpdateLockingStrategy) lockingStrategy; + } + + @Override + public boolean tryUpdateLock() { + return rwuls().tryUpdateLock(access, accessObj, offset); + } + + @Override + public boolean tryUpgradeReadToUpdateLock() { + return rwuls().tryUpgradeReadToUpdateLock(access, accessObj, offset); + } + + @Override + public boolean tryUpgradeUpdateToWriteLock() { + return rwuls().tryUpgradeUpdateToWriteLock(access, accessObj, offset); + } + + @Override + public void updateUnlock() { + rwuls().updateUnlock(access, accessObj, offset); + } + + @Override + public void downgradeUpdateToReadLock() { + rwuls().downgradeUpdateToReadLock(access, accessObj, offset); + } + + @Override + public void downgradeWriteToUpdateLock() { + rwuls().downgradeWriteToUpdateLock(access, accessObj, offset); + } + + @Override + public ReadWriteUpdateLockingStrategy lockingStrategy() { + return rwuls(); + } + } + TestReadWriteUpdateLockState rwuLockState = new TestReadWriteUpdateLockState(); + + Runnable readUnlockTask = new Runnable() { + @Override + public void run() { + rwls().readUnlock(); + } + }; + + Callable<Boolean> tryUpdateLockTask = new Callable<Boolean>() { + @Override + public Boolean call() { + return rwuls().tryUpdateLock(); + } + }; + + ReadWriteUpdateLockState rwuls() { + return rwuLockState; + } + + Runnable updateUnlockTask = new Runnable() { + @Override + public void run() { + rwuls().updateUnlock(); + } + }; + + Callable<Boolean> tryWriteLockTask = new Callable<Boolean>() { + @Override + public Boolean call() { + return rwls().tryWriteLock(); + } + }; + + Runnable writeUnlockTask = new Runnable() { + @Override + public void run() { + rwls().writeUnlock(); + } + }; + + @Test + public void testUpdateLockIsExclusive() throws ExecutionException, InterruptedException { + assumeReadWriteUpdateLock(); + + // Acquire the update lock in thread 1... + assertTrue(e1.submit(tryUpdateLockTask).get()); + + // Try to acquire update lock in thread 2, should fail... + assertFalse(e2.submit(tryUpdateLockTask).get()); + + // Release the update lock in thread 1... + e1.submit(updateUnlockTask).get(); + + // Try to acquire update lock in thread 2 again, should succeed... + assertTrue(e2.submit(tryUpdateLockTask).get()); + + // Release the update lock in thread 2... + e2.submit(updateUnlockTask).get(); + } + + @Test + public void testUpdateLockAllowsOtherReaders() throws ExecutionException, InterruptedException { + assumeReadWriteUpdateLock(); + + // Acquire the update lock in thread 1... + assertTrue(e1.submit(tryUpdateLockTask).get()); + + // Try to acquire read lock in thread 2, should succeed... + assertTrue(e2.submit(tryReadLockTask).get()); + + // Release the update lock in thread 1... + e1.submit(updateUnlockTask).get(); + + // Release the read lock in thread 2... + e2.submit(readUnlockTask).get(); + } + + @Test + public void testUpdateLockBlocksOtherWriters() throws ExecutionException, InterruptedException { + assumeReadWriteUpdateLock(); + + // Acquire the update lock in thread 1... + assertTrue(e1.submit(tryUpdateLockTask).get()); + + // Try to acquire write lock in thread 2, should fail... + assertFalse(e2.submit(tryWriteLockTask).get()); + + // Release the update lock in thread 1... + e1.submit(updateUnlockTask).get(); + + // Try to acquire write lock in thread 2 again, should succeed... + assertTrue(e2.submit(tryWriteLockTask).get()); + + // Release the write lock in thread 2... + e2.submit(writeUnlockTask).get(); + } + + @Test + public void testWriteLockBlocksOtherReaders() throws ExecutionException, InterruptedException { + assumeReadWriteLock(); + + // Acquire the write lock in thread 1... + assertTrue(e1.submit(tryWriteLockTask).get()); + + // Try to acquire read lock in thread 2, should fail... + assertFalse(e2.submit(tryReadLockTask).get()); + + // Release the write lock in thread 1... + e1.submit(writeUnlockTask).get(); + + // Try to acquire read lock in thread 2 again, should succeed... + assertTrue(e2.submit(tryReadLockTask).get()); + + // Release the read lock in thread 2... + e2.submit(readUnlockTask).get(); + } + + @Test + public void testUpdateLockUpgradeToWriteLock() throws ExecutionException, InterruptedException { + assumeReadWriteUpdateLock(); + + // Acquire the update lock in thread 1... + assertTrue(e1.submit(tryUpdateLockTask).get()); + + // Try to acquire write lock in thread 1, should succeed... + assertTrue(e1.submit(new Callable<Boolean>() { + @Override + public Boolean call() { + return rwuls().tryUpgradeUpdateToWriteLock(); + } + }).get()); + + // Release the write lock in thread 1... + e1.submit(new Runnable() { + @Override + public void run() { + rwuls().downgradeWriteToUpdateLock(); + } + }); + + // Release the update lock in thread 1... + e1.submit(updateUnlockTask).get(); + } + + @Test + public void testReadWriteLockTransitions() { + assumeReadWriteLock(); + + // forbid upgrades/downgrades/unlocks when lock is not held + readUnlockForbidden(); + writeUnlockForbidden(); + upgradeReadToWriteLockForbidden(); + downgradeWriteToReadLockForbidden(); + + // Read lock is held + assertTrue(rwls().tryReadLock()); + writeUnlockForbidden(); + downgradeWriteToReadLockForbidden(); + + // allow unlock + rwls().readUnlock(); + assertTrue(rwls().tryReadLock()); + + // allow upgrade to write lock + try { + assertTrue(rwls().tryUpgradeReadToWriteLock()); + } catch (UnsupportedOperationException tolerated) { + rwls().readUnlock(); + assertTrue(rwls().tryWriteLock()); + } + + // write lock is held + readUnlockForbidden(); + upgradeReadToWriteLockForbidden(); + + // allow unlock + rwls().writeUnlock(); + assertTrue(rwls().tryWriteLock()); + + // allow downgrade to read lock + try { + rwls().downgradeWriteToReadLock(); + } catch (UnsupportedOperationException tolerated) {} + + rwls().reset(); + } + + void downgradeWriteToReadLockForbidden() { + try { + rwls().downgradeWriteToReadLock(); + fail("downgradeWriteToReadLock() should fail"); + } catch (IllegalMonitorStateException e) { + // expected + } catch (UnsupportedOperationException e2) { + // expected + } + } + + void upgradeReadToWriteLockForbidden() { + try { + rwls().tryUpgradeReadToWriteLock(); + fail("tryUpgradeReadToWriteLock() should fail"); + } catch (IllegalMonitorStateException e) { + // expected + } catch (UnsupportedOperationException e2) { + // expected + } + } + + void writeUnlockForbidden() { + try { + rwls().writeUnlock(); + fail("writeUnlock() should fail"); + } catch (IllegalMonitorStateException e) { + // expected + } catch (UnsupportedOperationException e2) { + // expected + } + } + + void readUnlockForbidden() { + try { + rwls().readUnlock(); + fail("readUnlock() should fail"); + } catch (IllegalMonitorStateException e) { + // expected + } catch (UnsupportedOperationException e2) { + // expected + } + } + + @Test + public void testReadWriteUpgradeLockTransitions() { + assumeReadWriteUpdateLock(); + + // forbid upgrades/downgrades/unlocks when lock is not held + updateUnlockForbidden(); + upgradeReadToUpdateLockForbidden(); + upgradeUpdateToWriteLockForbidden(); + downgradeUpdateToReadLockForbidden(); + downgradeWriteToUpdateLockForbidden(); + + // Read lock is held + assertTrue(rwuls().tryReadLock()); + updateUnlockForbidden(); + upgradeUpdateToWriteLockForbidden(); + downgradeUpdateToReadLockForbidden(); + downgradeWriteToUpdateLockForbidden(); + + // allow upgrade to update lock + assertTrue(rwuls().tryUpgradeReadToUpdateLock()); + + // update lock is held + readUnlockForbidden(); + writeUnlockForbidden(); + upgradeReadToUpdateLockForbidden(); + upgradeReadToWriteLockForbidden(); + downgradeWriteToUpdateLockForbidden(); + downgradeWriteToReadLockForbidden(); + + // allow unlock + rwuls().updateUnlock(); + assertTrue(rwuls().tryUpdateLock()); + + // allow upgrade to write lock + assertTrue(rwuls().tryUpgradeUpdateToWriteLock()); + + // write lock is held + updateUnlockForbidden(); + upgradeReadToUpdateLockForbidden(); + upgradeUpdateToWriteLockForbidden(); + downgradeUpdateToReadLockForbidden(); + + // allow downgrade to update lock + rwuls().downgradeWriteToUpdateLock(); + + rwuls().updateUnlock(); + } + + void downgradeWriteToUpdateLockForbidden() { + try { + rwuls().downgradeWriteToUpdateLock(); + fail("downgradeWriteToUpdateLock() should fail"); + } catch (IllegalMonitorStateException e) { + // expected + } catch (UnsupportedOperationException e2) { + // expected + } + } + + void downgradeUpdateToReadLockForbidden() { + try { + rwuls().downgradeUpdateToReadLock(); + fail("downgradeUpdateToReadLock() should fail"); + } catch (IllegalMonitorStateException e) { + // expected + } catch (UnsupportedOperationException e2) { + // expected + } + } + + void upgradeUpdateToWriteLockForbidden() { + try { + rwuls().tryUpgradeUpdateToWriteLock(); + fail("tryUpgradeUpdateToWriteLock() should fail"); + } catch (IllegalMonitorStateException e) { + // expected + } catch (UnsupportedOperationException e2) { + // expected + } + } + + void upgradeReadToUpdateLockForbidden() { + try { + rwuls().tryUpgradeReadToUpdateLock(); + fail("tryUpgradeReadToUpdateLock() should fail"); + } catch (IllegalMonitorStateException e) { + // expected + } catch (UnsupportedOperationException e2) { + // expected + } + } + + void updateUnlockForbidden() { + try { + rwuls().updateUnlock(); + fail("updateUnlock() should fail"); + } catch (IllegalMonitorStateException e) { + // expected + } catch (UnsupportedOperationException e2) { + // expected + } + } + + void assumeReadWriteUpdateLock() { + assumeTrue(lockingStrategy instanceof ReadWriteUpdateLockingStrategy); + } + + void assumeReadWriteLock() { + assumeTrue(lockingStrategy instanceof ReadWriteLockingStrategy); + } +}
\ No newline at end of file diff --git a/lang/src/test/java/net/openhft/lang/model/DataValueGeneratorTest.java b/lang/src/test/java/net/openhft/lang/model/DataValueGeneratorTest.java index 1310fde..111df21 100644..100755 --- a/lang/src/test/java/net/openhft/lang/model/DataValueGeneratorTest.java +++ b/lang/src/test/java/net/openhft/lang/model/DataValueGeneratorTest.java @@ -1,17 +1,17 @@ /* - * Copyright 2013 Peter Lawrey + * Copyright (C) 2015 higherfrequencytrading.com * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as published by + * the Free Software Foundation, either version 3 of the License. * - * http://www.apache.org/licenses/LICENSE-2.0 + * 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 Lesser General Public License for more details. * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. + * You should have received a copy of the GNU Lesser General Public License + * along with this program. If not, see <http://www.gnu.org/licenses/>. */ package net.openhft.lang.model; @@ -22,6 +22,7 @@ import net.openhft.lang.io.Bytes; import org.junit.Test; import java.nio.ByteBuffer; +import java.util.Date; import static org.junit.Assert.assertEquals; import static org.junit.Assert.assertTrue; @@ -31,9 +32,9 @@ import static org.junit.Assert.assertTrue; */ public class DataValueGeneratorTest { @Test - public void testGenerateJavaCode() throws Exception { + public void testGenerateJavaCode() { DataValueGenerator dvg = new DataValueGenerator(); -// dvg.setDumpCode(true); + //dvg.setDumpCode(true); JavaBeanInterface jbi = dvg.heapInstance(JavaBeanInterface.class); jbi.setByte((byte) 1); jbi.setChar('2'); @@ -43,6 +44,7 @@ public class DataValueGeneratorTest { jbi.setLong(6); jbi.setDouble(7); jbi.setFlag(true); + assertEquals(1, jbi.getByte()); assertEquals('2', jbi.getChar()); assertEquals(3, jbi.getShort()); @@ -54,7 +56,7 @@ public class DataValueGeneratorTest { } @Test - public void testGenerateJavaCode2() throws Exception { + public void testGenerateJavaCode2() { DataValueGenerator dvg = new DataValueGenerator(); MinimalInterface mi = dvg.heapInstance(MinimalInterface.class); @@ -76,7 +78,7 @@ public class DataValueGeneratorTest { assertEquals(7.0, mi.double$(), 0.0); assertTrue(mi.flag()); - ByteBufferBytes bbb = new ByteBufferBytes(ByteBuffer.allocate(64)); + Bytes bbb = ByteBufferBytes.wrap(ByteBuffer.allocate(64)); mi.writeMarshallable(bbb); System.out.println("size: " + bbb.position()); @@ -84,7 +86,6 @@ public class DataValueGeneratorTest { bbb.position(0); mi2.readMarshallable(bbb); - assertEquals(1, mi2.byte$()); assertEquals('2', mi2.char$()); assertEquals(3, mi2.short$()); @@ -96,36 +97,44 @@ public class DataValueGeneratorTest { } @Test - public void testGenerateNative() throws Exception { - String actual = new DataValueGenerator().generateNativeObject(JavaBeanInterface.class); -// System.out.println(actual); + public void testGenerateNativeWithGetUsing() throws ClassNotFoundException, IllegalAccessException, InstantiationException { + String actual = new DataValueGenerator().generateNativeObject(JavaBeanInterfaceGetUsing.class); + System.out.println(actual); CachedCompiler cc = new CachedCompiler(null, null); - Class aClass = cc.loadFromJava(JavaBeanInterface.class.getName() + "£native", actual); - JavaBeanInterface jbi = (JavaBeanInterface) aClass.asSubclass(JavaBeanInterface.class).newInstance(); - Bytes bytes = new ByteBufferBytes(ByteBuffer.allocate(64)); - ((Byteable) jbi).bytes(bytes); - jbi.setByte((byte) 1); - jbi.setChar('2'); - jbi.setShort((short) 3); - jbi.setInt(4); - jbi.setFloat(5); - jbi.setLong(6); - jbi.setDouble(7); - jbi.setFlag(true); - assertEquals("", jbi.getString()); + Class aClass = cc.loadFromJava(JavaBeanInterfaceGetUsing.class.getName() + "$$Native", actual); + JavaBeanInterfaceGetUsing jbi = (JavaBeanInterfaceGetUsing) aClass.asSubclass(JavaBeanInterfaceGetUsing.class).newInstance(); + Bytes bytes = ByteBufferBytes.wrap(ByteBuffer.allocate(64)); + ((Byteable) jbi).bytes(bytes, 0L); + jbi.setString("G'day"); - assertEquals(1, jbi.getByte()); - assertEquals('2', jbi.getChar()); - assertEquals(3, jbi.getShort()); - assertEquals(4, jbi.getInt()); - assertEquals(5.0, jbi.getFloat(), 0); - assertEquals(6, jbi.getLong()); - assertEquals(7.0, jbi.getDouble(), 0.0); - assertTrue(jbi.getFlag()); - assertEquals("G'day", jbi.getString()); - assertEquals(42, ((Byteable) jbi).maxSize()); + + assertEquals("G'day", jbi.getUsingString(new StringBuilder()).toString()); + } + + @Test + public void testGenerateNativeWithHasArrays() throws ClassNotFoundException, IllegalAccessException, InstantiationException { + String actual = new DataValueGenerator().generateNativeObject(HasArraysInterface.class); + System.out.println(actual); + CachedCompiler cc = new CachedCompiler(null, null); + Class aClass = cc.loadFromJava(HasArraysInterface.class.getName() + "$$Native", actual); + HasArraysInterface hai = (HasArraysInterface) aClass.asSubclass(HasArraysInterface.class).newInstance(); + Bytes bytes = ByteBufferBytes.wrap(ByteBuffer.allocate(152)); + ((Byteable) hai).bytes(bytes, 0L); + + hai.setStringAt(0, "G'day"); + + assertEquals("G'day", hai.getStringAt(0)); } + @Test + public void testGenerateNativeWithGetUsingHeapInstance() { + DataValueGenerator dvg = new DataValueGenerator(); + JavaBeanInterfaceGetUsingHeap si = dvg.heapInstance(JavaBeanInterfaceGetUsingHeap.class); + + si.setString("G'day"); + + assertEquals("G'day", si.getUsingString(new StringBuilder()).toString()); + } @Test public void testStringFields() { @@ -135,8 +144,8 @@ public class DataValueGeneratorTest { assertEquals("Hello world", si.getString()); StringInterface si2 = dvg.nativeInstance(StringInterface.class); - Bytes bytes = new ByteBufferBytes(ByteBuffer.allocate(192)); - ((Byteable) si2).bytes(bytes); + Bytes bytes = ByteBufferBytes.wrap(ByteBuffer.allocate(192)); + ((Byteable) si2).bytes(bytes, 0L); si2.setString("Hello world £€"); si2.setText("Hello world £€"); assertEquals("Hello world £€", si2.getString()); @@ -144,6 +153,25 @@ public class DataValueGeneratorTest { } @Test + public void testGetUsingStringFieldsWithStringBuilderHeapInstance() { + DataValueGenerator dvg = new DataValueGenerator(); + GetUsingStringInterface si = dvg.heapInstance(GetUsingStringInterface.class); + si.setSomeStringField("Hello world"); + si.setAnotherStringField("Hello world 2"); + assertEquals("Hello world", si.getSomeStringField()); + { + StringBuilder builder = new StringBuilder(); + si.getUsingSomeStringField(builder); + assertEquals("Hello world", builder.toString()); + } + { + StringBuilder builder = new StringBuilder(); + si.getUsingAnotherStringField(builder); + assertEquals("Hello world 2", builder.toString()); + } + } + + @Test public void testNested() { DataValueGenerator dvg = new DataValueGenerator(); // dvg.setDumpCode(true); @@ -156,8 +184,8 @@ public class DataValueGeneratorTest { // dvg.setDumpCode(true); NestedA nestedA = dvg.nativeInstance(NestedA.class); - Bytes bytes = new ByteBufferBytes(ByteBuffer.allocate(192)); - ((Byteable) nestedA).bytes(bytes); + Bytes bytes = ByteBufferBytes.wrap(ByteBuffer.allocate(192)); + ((Byteable) nestedA).bytes(bytes, 0L); nestedA.key("key"); nestedA.one(nestedB1); nestedA.two(nestedB2); @@ -171,4 +199,41 @@ public class DataValueGeneratorTest { assertEquals(nestedB1.hashCode(), nestedA.one().hashCode()); assertEquals(nestedB2.hashCode(), nestedA.two().hashCode()); } + + @Test + public void testGenerateInterfaceWithEnumOnHeap() { + DataValueGenerator dvg = new DataValueGenerator(); + //dvg.setDumpCode(true); + JavaBeanInterfaceGetMyEnum jbie = dvg.heapInstance(JavaBeanInterfaceGetMyEnum.class); + jbie.setMyEnum(MyEnum.B); + } + + @Test + public void testGenerateInterfaceWithEnumNativeInstance() { + DataValueGenerator dvg = new DataValueGenerator(); + //dvg.setDumpCode(true); + JavaBeanInterfaceGetMyEnum jbie = dvg.nativeInstance(JavaBeanInterfaceGetMyEnum.class); + Bytes bytes = ByteBufferBytes.wrap(ByteBuffer.allocate(64)); + ((Byteable) jbie).bytes(bytes, 0L); + jbie.setMyEnum(MyEnum.C); + } + + @Test + public void testGenerateInterfaceWithDateOnHeap() { + DataValueGenerator dvg = new DataValueGenerator(); + //dvg.setDumpCode(true); + JavaBeanInterfaceGetDate jbid = dvg.heapInstance(JavaBeanInterfaceGetDate.class); + jbid.setDate(new Date()); + } + + @Test + public void testGenerateInterfaceWithDateNativeInstace() { + DataValueGenerator dvg = new DataValueGenerator(); + //dvg.setDumpCode(true); + JavaBeanInterfaceGetDate jbid = dvg.nativeInstance(JavaBeanInterfaceGetDate.class); + Bytes bytes = ByteBufferBytes.wrap(ByteBuffer.allocate(64)); + ((Byteable) jbid).bytes(bytes, 0L); + jbid.setDate(new Date()); + assertEquals(new Date(), jbid.getDate()); + } } diff --git a/lang/src/test/java/net/openhft/lang/model/DataValueModelTest.java b/lang/src/test/java/net/openhft/lang/model/DataValueModelTest.java index 0f7a8f0..14e1352 100644 --- a/lang/src/test/java/net/openhft/lang/model/DataValueModelTest.java +++ b/lang/src/test/java/net/openhft/lang/model/DataValueModelTest.java @@ -1,17 +1,17 @@ /* - * Copyright 2013 Peter Lawrey + * Copyright (C) 2015 higherfrequencytrading.com * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as published by + * the Free Software Foundation, either version 3 of the License. * - * http://www.apache.org/licenses/LICENSE-2.0 + * 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 Lesser General Public License for more details. * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. + * You should have received a copy of the GNU Lesser General Public License + * along with this program. If not, see <http://www.gnu.org/licenses/>. */ package net.openhft.lang.model; @@ -37,23 +37,35 @@ public class DataValueModelTest { " short$=FieldModel{name='short$', getter=public abstract short net.openhft.lang.model.MinimalInterface.short$(), setter=public abstract void net.openhft.lang.model.MinimalInterface.short$(short)}}" , midvm.fieldMap().toString().replaceAll("},", "}\n")); DataValueModel<JavaBeanInterface> jbdvm = DataValueModels.acquireModel(JavaBeanInterface.class); - assertEquals("{byte=FieldModel{name='byte', getter=public abstract byte net.openhft.lang.model.JavaBeanInterface.getByte(), setter=public abstract void net.openhft.lang.model.JavaBeanInterface.setByte(byte)}\n" + + + assertEquals("{byte=FieldModel{name='byte', getter=public abstract byte net.openhft.lang.model.JavaBeanInterface.getByte(), setter=public abstract void net.openhft.lang.model.JavaBeanInterface.setByte(byte)}\n" + " char=FieldModel{name='char', getter=public abstract char net.openhft.lang.model.JavaBeanInterface.getChar(), setter=public abstract void net.openhft.lang.model.JavaBeanInterface.setChar(char)}\n" + " double=FieldModel{name='double', getter=public abstract double net.openhft.lang.model.JavaBeanInterface.getDouble(), setter=public abstract void net.openhft.lang.model.JavaBeanInterface.setDouble(double)}\n" + " flag=FieldModel{name='flag', getter=public abstract boolean net.openhft.lang.model.JavaBeanInterface.getFlag(), setter=public abstract void net.openhft.lang.model.JavaBeanInterface.setFlag(boolean)}\n" + " float=FieldModel{name='float', getter=public abstract float net.openhft.lang.model.JavaBeanInterface.getFloat(), setter=public abstract void net.openhft.lang.model.JavaBeanInterface.setFloat(float)}\n" + " int=FieldModel{name='int', getter=public abstract int net.openhft.lang.model.JavaBeanInterface.getInt(), setter=public abstract void net.openhft.lang.model.JavaBeanInterface.setInt(int)}\n" + " long=FieldModel{name='long', getter=public abstract long net.openhft.lang.model.JavaBeanInterface.getLong(), setter=public abstract void net.openhft.lang.model.JavaBeanInterface.setLong(long)}\n" + - " record=FieldModel{name='record', getter=public abstract int net.openhft.lang.model.JavaBeanInterface.getRecord(), setter=public abstract void net.openhft.lang.model.JavaBeanInterface.setRecord(int)}\n" + + " record=FieldModel{name='record', getter=null, setter=null, busyLock=public abstract void net.openhft.lang.model.JavaBeanInterface.busyLockRecord() throws java.lang.InterruptedException, tryLock=public abstract boolean net.openhft.lang.model.JavaBeanInterface.tryLockRecord(), unlock=public abstract void net.openhft.lang.model.JavaBeanInterface.unlockRecord()}\n" + " short=FieldModel{name='short', getter=public abstract short net.openhft.lang.model.JavaBeanInterface.getShort(), setter=public abstract void net.openhft.lang.model.JavaBeanInterface.setShort(short)}\n" + " string=FieldModel{name='string', getter=public abstract java.lang.String net.openhft.lang.model.JavaBeanInterface.getString(), setter=public abstract void net.openhft.lang.model.JavaBeanInterface.setString(java.lang.String), size= @net.openhft.lang.model.constraints.MaxSize(value=8)}}" , jbdvm.fieldMap().toString().replaceAll("},", "}\n")); DataValueModel<NestedArrayInterface> nais = DataValueModels.acquireModel(NestedArrayInterface.class); - assertEquals("{int=FieldModel{name='int', getter=public abstract int net.openhft.lang.model.NestedArrayInterface.getInt(int), setter=public abstract void net.openhft.lang.model.NestedArrayInterface.setInt(int,int), indexSize= @net.openhft.lang.model.constraints.MaxSize(value=16)}\n" + - " jBI=FieldModel{name='jBI', getter=public abstract net.openhft.lang.model.JavaBeanInterface net.openhft.lang.model.NestedArrayInterface.getJBI(int), setter=public abstract void net.openhft.lang.model.NestedArrayInterface.setJBI(int,net.openhft.lang.model.JavaBeanInterface), indexSize= @net.openhft.lang.model.constraints.MaxSize(value=32)}\n" + + assertEquals("{int=FieldModel{name='int', getter=public abstract int net.openhft.lang.model.NestedArrayInterface.getIntAt(int), setter=public abstract void net.openhft.lang.model.NestedArrayInterface.setIntAt(int,int), indexSize= MaxSize(value=16)}\n" + + " jBI=FieldModel{name='jBI', getter=public abstract net.openhft.lang.model.JavaBeanInterface net.openhft.lang.model.NestedArrayInterface.getJBIAt(int), setter=public abstract void net.openhft.lang.model.NestedArrayInterface.setJBIAt(int,net.openhft.lang.model.JavaBeanInterface), indexSize= MaxSize(value=32)}\n" + " text=FieldModel{name='text', getter=public abstract java.lang.String net.openhft.lang.model.NestedArrayInterface.getText(), setter=public abstract void net.openhft.lang.model.NestedArrayInterface.setText(java.lang.String)}}" , nais.fieldMap().toString().replaceAll("},", "}\n")); + DataValueModel<HasArraysInterface> haim = DataValueModels.acquireModel(HasArraysInterface.class); + assertEquals("{byte=FieldModel{name='byte', getter=public abstract byte net.openhft.lang.model.HasArraysInterface.getByteAt(int), setter=public abstract void net.openhft.lang.model.HasArraysInterface.setByteAt(int,byte), indexSize= MaxSize(value=4)}\n" + + " char=FieldModel{name='char', getter=public abstract char net.openhft.lang.model.HasArraysInterface.getCharAt(int), setter=public abstract void net.openhft.lang.model.HasArraysInterface.setCharAt(int,char), indexSize= MaxSize(value=4)}\n" + + " double=FieldModel{name='double', getter=public abstract double net.openhft.lang.model.HasArraysInterface.getDoubleAt(int), setter=public abstract void net.openhft.lang.model.HasArraysInterface.setDoubleAt(int,double), indexSize= MaxSize(value=4)}\n" + + " flag=FieldModel{name='flag', getter=public abstract boolean net.openhft.lang.model.HasArraysInterface.getFlagAt(int), setter=public abstract void net.openhft.lang.model.HasArraysInterface.setFlagAt(int,boolean), indexSize= MaxSize(value=4)}\n" + + " float=FieldModel{name='float', getter=public abstract float net.openhft.lang.model.HasArraysInterface.getFloatAt(int), setter=public abstract void net.openhft.lang.model.HasArraysInterface.setFloatAt(int,float), indexSize= MaxSize(value=4)}\n" + + " int=FieldModel{name='int', getter=public abstract int net.openhft.lang.model.HasArraysInterface.getIntAt(int), setter=public abstract void net.openhft.lang.model.HasArraysInterface.setIntAt(int,int), indexSize= MaxSize(value=4)}\n" + + " long=FieldModel{name='long', getter=public abstract long net.openhft.lang.model.HasArraysInterface.getLongAt(int), setter=public abstract void net.openhft.lang.model.HasArraysInterface.setLongAt(int,long), indexSize= MaxSize(value=4)}\n" + + " short=FieldModel{name='short', getter=public abstract short net.openhft.lang.model.HasArraysInterface.getShortAt(int), setter=public abstract void net.openhft.lang.model.HasArraysInterface.setShortAt(int,short), indexSize= MaxSize(value=4)}\n" + + " string=FieldModel{name='string', getter=public abstract java.lang.String net.openhft.lang.model.HasArraysInterface.getStringAt(int), setter=public abstract void net.openhft.lang.model.HasArraysInterface.setStringAt(int,java.lang.String), size= @net.openhft.lang.model.constraints.MaxSize(value=8), indexSize= MaxSize(value=4)}}" + , haim.fieldMap().toString().replaceAll("},", "}\n")); } } diff --git a/lang/src/test/java/net/openhft/lang/model/FirstPrimitiveFieldTest.java b/lang/src/test/java/net/openhft/lang/model/FirstPrimitiveFieldTest.java new file mode 100644 index 0000000..12c98c7 --- /dev/null +++ b/lang/src/test/java/net/openhft/lang/model/FirstPrimitiveFieldTest.java @@ -0,0 +1,56 @@ +/* + * Copyright (C) 2015 higherfrequencytrading.com + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as published by + * the Free Software Foundation, either version 3 of the License. + * + * 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 Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with this program. If not, see <http://www.gnu.org/licenses/>. + */ + +package net.openhft.lang.model; + +import net.openhft.lang.model.constraints.MaxSize; +import net.openhft.lang.values.IntValue; +import net.openhft.lang.values.LongValue; +import org.junit.Test; + +import static net.openhft.lang.model.DataValueGenerator.firstPrimitiveFieldType; +import static org.junit.Assert.assertEquals; + +public class FirstPrimitiveFieldTest { + + @Test + public void firstPrimitiveFieldTest() { + assertEquals(int.class, firstPrimitiveFieldType(IntValue.class)); + assertEquals(long.class, firstPrimitiveFieldType(LongValue.class)); + assertEquals(long.class, + firstPrimitiveFieldType(DataValueClasses.directClassFor(LongValue.class))); + assertEquals(long.class, firstPrimitiveFieldType(FiveLongValues.class)); + assertEquals(boolean.class, firstPrimitiveFieldType(FiveBooleanValues.class)); + assertEquals(long.class, firstPrimitiveFieldType(FiveLongAndBooleanValues.class)); + } +} + +interface FiveLongValues { + void setValueAt(@MaxSize(5) int i, long v); + long getValueAt(int i); +} + +interface FiveBooleanValues { + void setValueAt(@MaxSize(5) int i, boolean v); + boolean getValueAt(int i); +} + +interface FiveLongAndBooleanValues { + void setLongValues(FiveLongValues values); + FiveLongValues getLongValues(); + void setBooleanValues(FiveBooleanValues values); + FiveBooleanValues getBooleanValues(); +} diff --git a/lang/src/test/java/net/openhft/lang/model/GetUsingStringInterface.java b/lang/src/test/java/net/openhft/lang/model/GetUsingStringInterface.java new file mode 100644 index 0000000..b5fc1c4 --- /dev/null +++ b/lang/src/test/java/net/openhft/lang/model/GetUsingStringInterface.java @@ -0,0 +1,36 @@ +/* + * Copyright (C) 2015 higherfrequencytrading.com + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as published by + * the Free Software Foundation, either version 3 of the License. + * + * 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 Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with this program. If not, see <http://www.gnu.org/licenses/>. + */ + +package net.openhft.lang.model; + +import net.openhft.lang.model.constraints.MaxSize; + +/** + * User: peter.lawrey Date: 08/10/13 Time: 09:09 + */ +public interface GetUsingStringInterface { + + void setSomeStringField(@MaxSize(64) String s); + + void setAnotherStringField(@MaxSize(64) String s); + + String getSomeStringField(); + + void getUsingSomeStringField(StringBuilder builder); + + StringBuilder getUsingAnotherStringField(StringBuilder builder); +} + diff --git a/lang/src/test/java/net/openhft/lang/model/HasArraysInterface.java b/lang/src/test/java/net/openhft/lang/model/HasArraysInterface.java new file mode 100644 index 0000000..422e90c --- /dev/null +++ b/lang/src/test/java/net/openhft/lang/model/HasArraysInterface.java @@ -0,0 +1,57 @@ +/* + * Copyright (C) 2015 higherfrequencytrading.com + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as published by + * the Free Software Foundation, either version 3 of the License. + * + * 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 Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with this program. If not, see <http://www.gnu.org/licenses/>. + */ + +package net.openhft.lang.model; + +import net.openhft.lang.model.constraints.MaxSize; + +public interface HasArraysInterface { + void setFlagAt(@MaxSize(4) int idx, boolean flag); + + boolean getFlagAt(int idx); + + void setByteAt(@MaxSize(4) int idx, byte b); + + byte getByteAt(int idx); + + void setShortAt(@MaxSize(4) int idx, short s); + + short getShortAt(int idx); + + void setCharAt(@MaxSize(4) int idx, char ch); + + char getCharAt(int idx); + + void setIntAt(@MaxSize(4) int idx, int i); + + int getIntAt(int idx); + + void setFloatAt(@MaxSize(4) int idx, float f); + + float getFloatAt(int idx); + + void setLongAt(@MaxSize(4) int idx, long l); + + long getLongAt(int idx); + + void setDoubleAt(@MaxSize(4) int idx, double d); + + double getDoubleAt(int idx); + + void setStringAt(@MaxSize(4) int idx, @MaxSize(8) String s); + + String getStringAt(int idx); +} diff --git a/lang/src/test/java/net/openhft/lang/model/JavaBeanInterface.java b/lang/src/test/java/net/openhft/lang/model/JavaBeanInterface.java index 0cf0675..ee6fe80 100644 --- a/lang/src/test/java/net/openhft/lang/model/JavaBeanInterface.java +++ b/lang/src/test/java/net/openhft/lang/model/JavaBeanInterface.java @@ -1,35 +1,28 @@ /* - * Copyright 2013 Peter Lawrey + * Copyright (C) 2015 higherfrequencytrading.com * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as published by + * the Free Software Foundation, either version 3 of the License. * - * http://www.apache.org/licenses/LICENSE-2.0 + * 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 Lesser General Public License for more details. * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. + * You should have received a copy of the GNU Lesser General Public License + * along with this program. If not, see <http://www.gnu.org/licenses/>. */ package net.openhft.lang.model; import net.openhft.lang.model.constraints.MaxSize; -/** - * User: peter.lawrey - * Date: 06/10/13 - * Time: 16:59 - */ public interface JavaBeanInterface { - int getRecord(); - - void setRecord(int record); - void busyLockRecord() throws InterruptedException; + boolean tryLockRecord(); + void unlockRecord(); void setFlag(boolean flag); @@ -50,6 +43,10 @@ public interface JavaBeanInterface { void setInt(int i); + int getVolatileInt(); + + void setOrderedInt(int i); + int getInt(); void setFloat(float f); @@ -60,11 +57,17 @@ public interface JavaBeanInterface { long getLong(); + long addAtomicLong(long toAdd); + void setDouble(double d); double getDouble(); + double addAtomicDouble(double toAdd); + void setString(@MaxSize(8) String s); String getString(); + + StringBuilder getUsingString(StringBuilder b); } diff --git a/lang/src/test/java/net/openhft/lang/model/JavaBeanInterfaceGetDate.java b/lang/src/test/java/net/openhft/lang/model/JavaBeanInterfaceGetDate.java new file mode 100644 index 0000000..f35f979 --- /dev/null +++ b/lang/src/test/java/net/openhft/lang/model/JavaBeanInterfaceGetDate.java @@ -0,0 +1,30 @@ +/* + * Copyright (C) 2015 higherfrequencytrading.com + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as published by + * the Free Software Foundation, either version 3 of the License. + * + * 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 Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with this program. If not, see <http://www.gnu.org/licenses/>. + */ + +package net.openhft.lang.model; + +import java.util.Date; + +/** + * Created by pct25 on 6/4/2015. + */ +public interface JavaBeanInterfaceGetDate { + + void setDate(Date date); + + Date getDate(); + +} diff --git a/lang/src/test/java/net/openhft/lang/model/JavaBeanInterfaceGetMyEnum.java b/lang/src/test/java/net/openhft/lang/model/JavaBeanInterfaceGetMyEnum.java new file mode 100644 index 0000000..ca96a34 --- /dev/null +++ b/lang/src/test/java/net/openhft/lang/model/JavaBeanInterfaceGetMyEnum.java @@ -0,0 +1,27 @@ +/* + * Copyright (C) 2015 higherfrequencytrading.com + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as published by + * the Free Software Foundation, either version 3 of the License. + * + * 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 Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with this program. If not, see <http://www.gnu.org/licenses/>. + */ + +package net.openhft.lang.model; + +/** + * Created by pct25 on 6/4/2015. + */ +public interface JavaBeanInterfaceGetMyEnum { + + void setMyEnum( MyEnum myEnum); + + MyEnum getMyEnum(); +} diff --git a/lang/src/test/java/net/openhft/lang/model/JavaBeanInterfaceGetUsing.java b/lang/src/test/java/net/openhft/lang/model/JavaBeanInterfaceGetUsing.java new file mode 100644 index 0000000..05ce14e --- /dev/null +++ b/lang/src/test/java/net/openhft/lang/model/JavaBeanInterfaceGetUsing.java @@ -0,0 +1,29 @@ +/* + * Copyright (C) 2015 higherfrequencytrading.com + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as published by + * the Free Software Foundation, either version 3 of the License. + * + * 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 Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with this program. If not, see <http://www.gnu.org/licenses/>. + */ + +package net.openhft.lang.model; + +import net.openhft.lang.model.constraints.MaxSize; + +/** + * User: peter.lawrey Date: 06/10/13 Time: 16:59 + */ +public interface JavaBeanInterfaceGetUsing { + + void setString(@MaxSize(8) String s); + + StringBuilder getUsingString(StringBuilder b); +} diff --git a/lang/src/test/java/net/openhft/lang/model/JavaBeanInterfaceGetUsingHeap.java b/lang/src/test/java/net/openhft/lang/model/JavaBeanInterfaceGetUsingHeap.java new file mode 100644 index 0000000..c90743f --- /dev/null +++ b/lang/src/test/java/net/openhft/lang/model/JavaBeanInterfaceGetUsingHeap.java @@ -0,0 +1,29 @@ +/* + * Copyright (C) 2015 higherfrequencytrading.com + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as published by + * the Free Software Foundation, either version 3 of the License. + * + * 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 Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with this program. If not, see <http://www.gnu.org/licenses/>. + */ + +package net.openhft.lang.model; + +import net.openhft.lang.model.constraints.MaxSize; + +/** + * User: peter.lawrey Date: 06/10/13 Time: 16:59 + */ +public interface JavaBeanInterfaceGetUsingHeap { + + void setString(@MaxSize(8) String s); + + StringBuilder getUsingString(StringBuilder b); +} diff --git a/lang/src/test/java/net/openhft/lang/model/MinimalInterface.java b/lang/src/test/java/net/openhft/lang/model/MinimalInterface.java index 2379b2e..d1362cc 100644 --- a/lang/src/test/java/net/openhft/lang/model/MinimalInterface.java +++ b/lang/src/test/java/net/openhft/lang/model/MinimalInterface.java @@ -1,17 +1,17 @@ /* - * Copyright 2013 Peter Lawrey + * Copyright (C) 2015 higherfrequencytrading.com * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as published by + * the Free Software Foundation, either version 3 of the License. * - * http://www.apache.org/licenses/LICENSE-2.0 + * 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 Lesser General Public License for more details. * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. + * You should have received a copy of the GNU Lesser General Public License + * along with this program. If not, see <http://www.gnu.org/licenses/>. */ package net.openhft.lang.model; diff --git a/lang/src/test/java/net/openhft/lang/model/MyEnum.java b/lang/src/test/java/net/openhft/lang/model/MyEnum.java new file mode 100644 index 0000000..a9b0e7a --- /dev/null +++ b/lang/src/test/java/net/openhft/lang/model/MyEnum.java @@ -0,0 +1,34 @@ +/* + * Copyright (C) 2015 higherfrequencytrading.com + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as published by + * the Free Software Foundation, either version 3 of the License. + * + * 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 Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with this program. If not, see <http://www.gnu.org/licenses/>. + */ + +package net.openhft.lang.model; + +/** + * Created by pct25 on 6/4/2015. + */ +public enum MyEnum { + A(1), B(2), C(3); + + private final int var; + + MyEnum(int var) { + this.var = var; + } + + public int getVar() { + return this.var; + } +} diff --git a/lang/src/test/java/net/openhft/lang/model/NestedA.java b/lang/src/test/java/net/openhft/lang/model/NestedA.java index 70d5f8a..e440615 100644 --- a/lang/src/test/java/net/openhft/lang/model/NestedA.java +++ b/lang/src/test/java/net/openhft/lang/model/NestedA.java @@ -1,17 +1,17 @@ /* - * Copyright 2013 Peter Lawrey + * Copyright (C) 2015 higherfrequencytrading.com * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as published by + * the Free Software Foundation, either version 3 of the License. * - * http://www.apache.org/licenses/LICENSE-2.0 + * 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 Lesser General Public License for more details. * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. + * You should have received a copy of the GNU Lesser General Public License + * along with this program. If not, see <http://www.gnu.org/licenses/>. */ package net.openhft.lang.model; @@ -35,5 +35,4 @@ public interface NestedA { void two(NestedB one); NestedB two(); - } diff --git a/lang/src/test/java/net/openhft/lang/model/NestedArrayInterface.java b/lang/src/test/java/net/openhft/lang/model/NestedArrayInterface.java index 22caef0..81d7dcc 100644 --- a/lang/src/test/java/net/openhft/lang/model/NestedArrayInterface.java +++ b/lang/src/test/java/net/openhft/lang/model/NestedArrayInterface.java @@ -1,17 +1,17 @@ /* - * Copyright 2013 Peter Lawrey + * Copyright (C) 2015 higherfrequencytrading.com * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as published by + * the Free Software Foundation, either version 3 of the License. * - * http://www.apache.org/licenses/LICENSE-2.0 + * 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 Lesser General Public License for more details. * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. + * You should have received a copy of the GNU Lesser General Public License + * along with this program. If not, see <http://www.gnu.org/licenses/>. */ package net.openhft.lang.model; @@ -25,11 +25,11 @@ public interface NestedArrayInterface { void setText(String text); - int getInt(int index); + int getIntAt(int index); - void setInt(@MaxSize(16) int index, int value); + void setIntAt(@MaxSize(16) int index, int value); - JavaBeanInterface getJBI(@MaxSize(32) int index); + JavaBeanInterface getJBIAt(@MaxSize(32) int index); - void setJBI(int index, JavaBeanInterface jbi); + void setJBIAt(int index, JavaBeanInterface jbi); } diff --git a/lang/src/test/java/net/openhft/lang/model/NestedB.java b/lang/src/test/java/net/openhft/lang/model/NestedB.java index 5b4807b..1465853 100644 --- a/lang/src/test/java/net/openhft/lang/model/NestedB.java +++ b/lang/src/test/java/net/openhft/lang/model/NestedB.java @@ -1,17 +1,17 @@ /* - * Copyright 2013 Peter Lawrey + * Copyright (C) 2015 higherfrequencytrading.com * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as published by + * the Free Software Foundation, either version 3 of the License. * - * http://www.apache.org/licenses/LICENSE-2.0 + * 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 Lesser General Public License for more details. * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. + * You should have received a copy of the GNU Lesser General Public License + * along with this program. If not, see <http://www.gnu.org/licenses/>. */ package net.openhft.lang.model; diff --git a/lang/src/test/java/net/openhft/lang/model/StringInterface.java b/lang/src/test/java/net/openhft/lang/model/StringInterface.java index 14d5d19..57a4526 100644 --- a/lang/src/test/java/net/openhft/lang/model/StringInterface.java +++ b/lang/src/test/java/net/openhft/lang/model/StringInterface.java @@ -1,17 +1,17 @@ /* - * Copyright 2013 Peter Lawrey + * Copyright (C) 2015 higherfrequencytrading.com * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as published by + * the Free Software Foundation, either version 3 of the License. * - * http://www.apache.org/licenses/LICENSE-2.0 + * 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 Lesser General Public License for more details. * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. + * You should have received a copy of the GNU Lesser General Public License + * along with this program. If not, see <http://www.gnu.org/licenses/>. */ package net.openhft.lang.model; diff --git a/lang/src/test/java/net/openhft/lang/model/VolatileTest.java b/lang/src/test/java/net/openhft/lang/model/VolatileTest.java new file mode 100644 index 0000000..9191549 --- /dev/null +++ b/lang/src/test/java/net/openhft/lang/model/VolatileTest.java @@ -0,0 +1,128 @@ +/* + * Copyright (C) 2015 higherfrequencytrading.com + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as published by + * the Free Software Foundation, either version 3 of the License. + * + * 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 Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with this program. If not, see <http://www.gnu.org/licenses/>. + */ + +package net.openhft.lang.model; + +import net.openhft.compiler.CachedCompiler; +import net.openhft.lang.io.ByteBufferBytes; +import net.openhft.lang.io.Bytes; +import net.openhft.lang.model.constraints.MaxSize; +import org.junit.Test; + +import java.nio.ByteBuffer; + +import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertFalse; + +/** + * Created by daniel on 11/06/2014. + */ +public class VolatileTest { + @Test + public void testGenerateJavaCode() throws ClassNotFoundException, IllegalAccessException, InstantiationException { + DataValueGenerator dvg = new DataValueGenerator(); + // dvg.setDumpCode(true); + + /* try{ + BadInterface1 jbi = dvg.heapInstance(BadInterface1.class); + assertFalse("Should have thrown an IllegalArgumentException", true); + }catch(AssertionError e){ + assertTrue("Throws an IllegalArgumentException", true); + } + + try{ + BadInterface2 jbi = dvg.heapInstance(BadInterface2.class); + assertFalse("Should have thrown an IllegalArgumentException", true); + }catch(AssertionError e){ + assertTrue("Throws an IllegalArgumentException", true); + } +*/ + //Test the heap interface + try{ + GoodInterface jbi = dvg.heapInstance(GoodInterface.class); + + jbi.setOrderedY(5); + assertEquals(5, jbi.getVolatileY()); + jbi.setOrderedIntAt(0,0); + jbi.setOrderedIntAt(1,1); + jbi.setOrderedIntAt(2,2); + jbi.setOrderedIntAt(3,3); + + assertEquals(0, jbi.getVolatileIntAt(0)); + assertEquals(1, jbi.getVolatileIntAt(1)); + assertEquals(2, jbi.getVolatileIntAt(2)); + assertEquals(3, jbi.getVolatileIntAt(3)); + }catch(AssertionError e){ + e.printStackTrace(); + assertFalse("Throws an IllegalArgumentException", true); + } + + //Test the native interface + try{ + String actual = new DataValueGenerator().generateNativeObject(GoodInterface.class); + System.out.println(actual); + CachedCompiler cc = new CachedCompiler(null, null); + Class aClass = cc.loadFromJava(GoodInterface.class.getName() + "$$Native", actual); + GoodInterface jbi = (GoodInterface) aClass.asSubclass(GoodInterface.class).newInstance(); + Bytes bytes = ByteBufferBytes.wrap(ByteBuffer.allocate(64)); + ((Byteable) jbi).bytes(bytes, 0L); + + jbi.setOrderedY(5); + assertEquals(5, jbi.getVolatileY()); + jbi.setOrderedIntAt(0,0); + jbi.setOrderedIntAt(1,1); + jbi.setOrderedIntAt(2,2); + jbi.setOrderedIntAt(3,3); + + assertEquals(0, jbi.getVolatileIntAt(0)); + assertEquals(1, jbi.getVolatileIntAt(1)); + assertEquals(2, jbi.getVolatileIntAt(2)); + assertEquals(3, jbi.getVolatileIntAt(3)); + }catch(AssertionError e){ + e.printStackTrace(); + assertFalse("Throws an IllegalArgumentException", true); + } + } + + public interface BadInterface1{ + int getX(); + + void setOrderedX(int x); + } + + public interface BadInterface2{ + int getVolatileX(); + + void setX(int x); + } + + public interface GoodInterface{ + int getX(); + + void setX(int x); + + int getVolatileY(); + + void setOrderedY(int y); + + int getY(); + + void setY(int y); + + void setOrderedIntAt(@MaxSize(4) int idx, int i); + int getVolatileIntAt(int idx); + } +} diff --git a/lang/src/test/java/net/openhft/lang/testing/RunningMinimumTest.java b/lang/src/test/java/net/openhft/lang/testing/RunningMinimumTest.java index a6c0ddb..916ccdc 100644 --- a/lang/src/test/java/net/openhft/lang/testing/RunningMinimumTest.java +++ b/lang/src/test/java/net/openhft/lang/testing/RunningMinimumTest.java @@ -1,17 +1,17 @@ /* - * Copyright 2013 Peter Lawrey + * Copyright (C) 2015 higherfrequencytrading.com * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as published by + * the Free Software Foundation, either version 3 of the License. * - * http://www.apache.org/licenses/LICENSE-2.0 + * 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 Lesser General Public License for more details. * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. + * You should have received a copy of the GNU Lesser General Public License + * along with this program. If not, see <http://www.gnu.org/licenses/>. */ package net.openhft.lang.testing; @@ -27,7 +27,7 @@ import static org.junit.Assert.assertEquals; */ public class RunningMinimumTest { @Test - public void testSample() throws Exception { + public void testSample() { for (int k = 0; k < 1000; k++) { for (long delta : new long[]{0, Integer.MIN_VALUE, Integer.MAX_VALUE}) { RunningMinimum rm = new RunningMinimum(50 * 1000); diff --git a/lang/src/test/java/net/openhft/lang/thread/LightPauserTest.java b/lang/src/test/java/net/openhft/lang/thread/LightPauserTest.java new file mode 100755 index 0000000..f37cfcb --- /dev/null +++ b/lang/src/test/java/net/openhft/lang/thread/LightPauserTest.java @@ -0,0 +1,48 @@ +/* + * Copyright (C) 2015 higherfrequencytrading.com + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as published by + * the Free Software Foundation, either version 3 of the License. + * + * 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 Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with this program. If not, see <http://www.gnu.org/licenses/>. + */ + +package net.openhft.lang.thread; + +import org.junit.Test; + +/** + * Created by peter.lawrey on 11/12/14. + */ +public class LightPauserTest { + @Test + public void testLightPauser() throws InterruptedException { + final LightPauser pauser = new LightPauser(100 * 1000, 100 * 1000); + Thread thread = new Thread() { + @Override + public void run() { + while (!Thread.interrupted()) + pauser.pause(); + } + }; + thread.start(); + + for (int t = 0; t < 3; t++) { + long start = System.nanoTime(); + int runs = 10000000; + for (int i = 0; i < runs; i++) + pauser.unpause(); + long time = System.nanoTime() - start; + System.out.printf("Average time to unpark was %,d ns%n", time / runs); + Thread.sleep(20); + } + thread.interrupt(); + } +} diff --git a/lang/src/test/java/net/openhft/lang/values/BuySell.java b/lang/src/test/java/net/openhft/lang/values/BuySell.java new file mode 100755 index 0000000..2a7b860 --- /dev/null +++ b/lang/src/test/java/net/openhft/lang/values/BuySell.java @@ -0,0 +1,5 @@ +package net.openhft.lang.values; + +enum BuySell { + Buy, Sell +}
\ No newline at end of file diff --git a/lang/src/test/java/net/openhft/lang/values/BuySellValues.java b/lang/src/test/java/net/openhft/lang/values/BuySellValues.java new file mode 100755 index 0000000..e85c9d6 --- /dev/null +++ b/lang/src/test/java/net/openhft/lang/values/BuySellValues.java @@ -0,0 +1,12 @@ +package net.openhft.lang.values; + +import net.openhft.lang.model.constraints.MaxSize; + +/** + * Created by peter.lawrey on 06/08/2015. + */ +public interface BuySellValues { + BuySell getValue(); + + void setValue(@MaxSize(5) BuySell buySell); +} diff --git a/lang/src/test/java/net/openhft/lang/values/CheckValuesBuildTest.java b/lang/src/test/java/net/openhft/lang/values/CheckValuesBuildTest.java index f10ae6e..52301fa 100644 --- a/lang/src/test/java/net/openhft/lang/values/CheckValuesBuildTest.java +++ b/lang/src/test/java/net/openhft/lang/values/CheckValuesBuildTest.java @@ -1,17 +1,17 @@ /* - * Copyright 2013 Peter Lawrey + * Copyright (C) 2015 higherfrequencytrading.com * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as published by + * the Free Software Foundation, either version 3 of the License. * - * http://www.apache.org/licenses/LICENSE-2.0 + * 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 Lesser General Public License for more details. * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. + * You should have received a copy of the GNU Lesser General Public License + * along with this program. If not, see <http://www.gnu.org/licenses/>. */ package net.openhft.lang.values; diff --git a/lang/src/test/java/net/openhft/lang/values/EnumValuesTest.java b/lang/src/test/java/net/openhft/lang/values/EnumValuesTest.java new file mode 100755 index 0000000..72d4471 --- /dev/null +++ b/lang/src/test/java/net/openhft/lang/values/EnumValuesTest.java @@ -0,0 +1,53 @@ +package net.openhft.lang.values; + +import net.openhft.lang.io.DirectBytes; +import net.openhft.lang.io.DirectStore; +import net.openhft.lang.io.serialization.BytesMarshallable; +import net.openhft.lang.model.DataValueClasses; +import org.junit.Assert; +import org.junit.Ignore; +import org.junit.Test; + +import static org.junit.Assert.assertNull; + +/** + * Created by peter.lawrey on 06/08/2015. + */ +public class EnumValuesTest { + @Test + public void testSetNull() { + BuySellValues value = DataValueClasses.newInstance(BuySellValues.class); + value.setValue(null); + assertNull(value.getValue()); + } + + @Test + public void testBytesMarshallable() { + BuySellValues value = DataValueClasses.newInstance(BuySellValues.class); + DirectBytes bytes = DirectStore.allocate(8).bytes(); + ((BytesMarshallable) value).writeMarshallable(bytes); + + bytes.clear(); + + BuySellValues value2 = DataValueClasses.newInstance(BuySellValues.class); + // to ensure, in assert below, that readMarshallable indeed reads and sets null + value2.setValue(BuySell.Sell); + ((BytesMarshallable) value2).readMarshallable(bytes); + assertNull(value2.getValue()); + } + + @Test + public void testBytesMarshallable2() { + BuySellValues value = DataValueClasses.newInstance(BuySellValues.class); + DirectBytes bytes = DirectStore.allocate(8).bytes(); + value.setValue(BuySell.Buy); + ((BytesMarshallable) value).writeMarshallable(bytes); + + bytes.clear(); + + BuySellValues value2 = DataValueClasses.newInstance(BuySellValues.class); + ((BytesMarshallable) value2).readMarshallable(bytes); + Assert.assertEquals(BuySell.Buy, value2.getValue()); + } +} + diff --git a/lang/src/test/java/net/openhft/lang/values/NestAll.java b/lang/src/test/java/net/openhft/lang/values/NestAll.java index cb629b6..10b2b6c 100644 --- a/lang/src/test/java/net/openhft/lang/values/NestAll.java +++ b/lang/src/test/java/net/openhft/lang/values/NestAll.java @@ -1,17 +1,17 @@ /* - * Copyright 2013 Peter Lawrey + * Copyright (C) 2015 higherfrequencytrading.com * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as published by + * the Free Software Foundation, either version 3 of the License. * - * http://www.apache.org/licenses/LICENSE-2.0 + * 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 Lesser General Public License for more details. * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. + * You should have received a copy of the GNU Lesser General Public License + * along with this program. If not, see <http://www.gnu.org/licenses/>. */ package net.openhft.lang.values; @@ -49,5 +49,4 @@ public interface NestAll { UnsignedIntValue getUI(); UnsignedShortValue getUS(); - } diff --git a/lang/src/test/java/net/openhft/lang/values/StringValueTest.java b/lang/src/test/java/net/openhft/lang/values/StringValueTest.java new file mode 100755 index 0000000..4ca867b --- /dev/null +++ b/lang/src/test/java/net/openhft/lang/values/StringValueTest.java @@ -0,0 +1,37 @@ +package net.openhft.lang.values; + +import net.openhft.lang.io.DirectBytes; +import net.openhft.lang.io.DirectStore; +import net.openhft.lang.io.serialization.BytesMarshallable; +import net.openhft.lang.model.DataValueClasses; +import org.junit.Ignore; +import org.junit.Test; + +import static org.junit.Assert.assertNull; + +/** + * Created by peter.lawrey on 06/08/2015. + */ +public class StringValueTest { + @Test + public void testSetNull() { + StringValue value = DataValueClasses.newInstance(StringValue.class); + value.setValue(null); + assertNull(value.getValue()); + } + + @Test + public void testBytesMarshallable() { + StringValue value = DataValueClasses.newInstance(StringValue.class); + DirectBytes bytes = DirectStore.allocate(8).bytes(); + ((BytesMarshallable) value).writeMarshallable(bytes); + + bytes.clear(); + + StringValue value2 = DataValueClasses.newInstance(StringValue.class); + // to ensure, in assert below, that readMarshallable indeed reads and sets null + value2.setValue("foo"); + ((BytesMarshallable) value2).readMarshallable(bytes); + assertNull(value2.getValue()); + } +}
\ No newline at end of file diff --git a/lang8/pom.xml b/lang8/pom.xml new file mode 100755 index 0000000..d2a2963 --- /dev/null +++ b/lang8/pom.xml @@ -0,0 +1,200 @@ +<?xml version="1.0" encoding="UTF-8"?> +<!-- + ~ Copyright (C) 2015 higherfrequencytrading.com + ~ + ~ This program is free software: you can redistribute it and/or modify + ~ it under the terms of the GNU Lesser General Public License as published by + ~ the Free Software Foundation, either version 3 of the License. + ~ + ~ 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 Lesser General Public License for more details. + ~ + ~ You should have received a copy of the GNU Lesser General Public License + ~ along with this program. If not, see <http://www.gnu.org/licenses/>. + --> + +<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" + xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd"> + + <parent> + <groupId>net.openhft</groupId> + <artifactId>java-parent-pom</artifactId> + <version>1.1.2</version> + <relativePath/> + </parent> + + <modelVersion>4.0.0</modelVersion> + <artifactId>lang</artifactId> + <version>8.0.0-SNAPSHOT</version> + <packaging>bundle</packaging> + <name>OpenHFT/Java-Lang/lang v8</name> + <description>Java Lang library for High Frequency Trading (Java 8+)</description> + + <dependencyManagement> + <dependencies> + <dependency> + <groupId>net.openhft</groupId> + <artifactId>third-party-bom</artifactId> + <type>pom</type> + <version>3.4.20</version> + <scope>import</scope> + </dependency> + <dependency> + <groupId>net.openhft</groupId> + <artifactId>compiler</artifactId> + <version>2.2.0</version> + </dependency> + + </dependencies> + </dependencyManagement> + + <dependencies> + + <dependency> + <groupId>org.ow2.asm</groupId> + <artifactId>asm</artifactId> + </dependency> + + <dependency> + <groupId>org.slf4j</groupId> + <artifactId>slf4j-api</artifactId> + </dependency> + + <dependency> + <groupId>com.sun.java</groupId> + <artifactId>tools</artifactId> + </dependency> + + <dependency> + <groupId>net.openhft</groupId> + <artifactId>compiler</artifactId> + </dependency> + + <dependency> + <groupId>org.xerial.snappy</groupId> + <artifactId>snappy-java</artifactId> + </dependency> + + <dependency> + <groupId>org.easymock</groupId> + <artifactId>easymock</artifactId> + <scope>test</scope> + </dependency> + + <dependency> + <groupId>junit</groupId> + <artifactId>junit</artifactId> + <scope>test</scope> + </dependency> + + <dependency> + <groupId>org.slf4j</groupId> + <artifactId>slf4j-simple</artifactId> + <scope>test</scope> + </dependency> + + </dependencies> + + <build> + + <plugins> + + <plugin> + <groupId>org.apache.maven.plugins</groupId> + <artifactId>maven-scm-publish-plugin</artifactId> + <configuration> + <checkoutDirectory>${project.build.directory}/scmpublish/javadoc</checkoutDirectory> + <checkinComment>Publishing javadoc for ${project.artifactId}:${project.version} + </checkinComment> + <content>${project.reporting.outputDirectory}</content> + <skipDeletedFiles>true</skipDeletedFiles> + <pubScmUrl>scm:git:git@github.com:OpenHFT/Java-Lang</pubScmUrl> + <scmBranch>gh-pages</scmBranch> + </configuration> + </plugin> + + <plugin> + <groupId>org.apache.felix</groupId> + <artifactId>maven-bundle-plugin</artifactId> + <extensions>true</extensions> + <configuration> + <instructions> + <Bundle-SymbolicName>${project.groupId}.${project.artifactId}</Bundle-SymbolicName> + <Bundle-Name>OpenHFT :: ${project.artifactId}</Bundle-Name> + <Bundle-Version>${project.version}</Bundle-Version> + <Export-Package> + net.openhft.lang.* + </Export-Package> + </instructions> + </configuration> + <executions> + <!-- + This execution makes sure that the manifest is available + when the tests are executed + --> + <execution> + <goals> + <goal>manifest</goal> + </goals> + </execution> + </executions> + </plugin> + + <plugin> + <groupId>org.apache.maven.plugins</groupId> + <artifactId>maven-compiler-plugin</artifactId> + <configuration> + <source>1.8</source> + <target>1.8</target> + </configuration> + </plugin> + </plugins> + </build> + + <profiles> + <profile> + <!-- to use this run with the profile --> + <id>pitest</id> + <build> + <plugins> + <plugin> + <!-- For debugging in IntelliJ IDEA --> + <groupId>org.apache.maven.plugins</groupId> + <artifactId>maven-surefire-plugin</artifactId> + <version>2.18.1</version> + <configuration> + <systemPropertyVariables> + <project.build.directory>${project.build.directory}</project.build.directory> + </systemPropertyVariables> + + <forkMode>never</forkMode> + </configuration> + </plugin> + <plugin> + <groupId>org.pitest</groupId> + <artifactId>pitest-maven</artifactId> + <version>0.33</version> + <configuration> + <targetClasses> + <param>net.openhft.lang.collection.*</param> + </targetClasses> + <targetTests> + <param>net.openhft.lang.collection.*</param> + </targetTests> + </configuration> + </plugin> + </plugins> + </build> + </profile> + </profiles> + + <scm> + <url>scm:git:git@github.com:OpenHFT/Java-Lang.git</url> + <connection>scm:git:git@github.com:OpenHFT/Java-Lang.git</connection> + <developerConnection>scm:git:git@github.com:OpenHFT/Java-Lang.git</developerConnection> + <tag>master</tag> + </scm> + +</project> @@ -1,28 +1,34 @@ <?xml version="1.0" encoding="UTF-8"?> <!-- - ~ Copyright 2013 Peter Lawrey + ~ Copyright (C) 2015 higherfrequencytrading.com ~ - ~ Licensed under the Apache License, Version 2.0 (the "License"); - ~ you may not use this file except in compliance with the License. - ~ You may obtain a copy of the License at + ~ This program is free software: you can redistribute it and/or modify + ~ it under the terms of the GNU Lesser General Public License as published by + ~ the Free Software Foundation, either version 3 of the License. ~ - ~ http://www.apache.org/licenses/LICENSE-2.0 + ~ 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 Lesser General Public License for more details. ~ - ~ Unless required by applicable law or agreed to in writing, software - ~ distributed under the License is distributed on an "AS IS" BASIS, - ~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - ~ See the License for the specific language governing permissions and - ~ limitations under the License. + ~ You should have received a copy of the GNU Lesser General Public License + ~ along with this program. If not, see <http://www.gnu.org/licenses/>. --> -<project xmlns="http://maven.apache.org/POM/4.0.0" - xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" +<project xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" + xmlns="http://maven.apache.org/POM/4.0.0" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd"> - <modelVersion>4.0.0</modelVersion> - <groupId>net.openhft</groupId> + <parent> + <groupId>net.openhft</groupId> + <artifactId>root-parent-pom</artifactId> + <version>1.1.1</version> + <relativePath/> + </parent> + + <modelVersion>4.0.0</modelVersion> <artifactId>Java-Lang</artifactId> - <version>6.1.3</version> + <version>6.7-SNAPSHOT</version> <packaging>pom</packaging> <name>Java Lang Parent</name> @@ -30,7 +36,17 @@ <modules> <module>lang</module> - <module>lang-osgi</module> - <module>lang-integration</module> + <!-- <module>lang-test</module> --> + <!--<module>lang-sandbox</module>--> </modules> -</project>
\ No newline at end of file + + + <scm> + <url>scm:git:git@github.com:OpenHFT/Java-Lang.git</url> + <connection>scm:git:git@github.com:OpenHFT/Java-Lang.git</connection> + <developerConnection>scm:git:git@github.com:OpenHFT/Java-Lang.git</developerConnection> + <tag>master</tag> + </scm> + +</project> + |