diff options
author | Emmanuel Bourg <ebourg@apache.org> | 2015-07-17 00:28:58 +0200 |
---|---|---|
committer | Emmanuel Bourg <ebourg@apache.org> | 2015-07-17 00:28:58 +0200 |
commit | dadc5626cd17e6dc4c4cc9fa814fbe2a65ac77d5 (patch) | |
tree | 266aefd3798c3db8c847e5e4f0be4d3ec4753bd9 | |
parent | 1b08101f81e5790c213a07dce165169861ef9d95 (diff) | |
parent | b2ec1a2d459cfef3ff13133c1f7f5972e3740258 (diff) |
Merge tag 'upstream/6.1.4'
Upstream version 6.1.4
133 files changed, 17124 insertions, 0 deletions
diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..e7c1ea9 --- /dev/null +++ b/.gitignore @@ -0,0 +1,13 @@ +*.class + +# Package Files # +*.jar +*.war +*.ear + +# IntelliJ +*.iml +.idea + +# maven +target diff --git a/.idea/copyright/Apache_2_0.xml b/.idea/copyright/Apache_2_0.xml new file mode 100644 index 0000000..8cd7a0e --- /dev/null +++ b/.idea/copyright/Apache_2_0.xml @@ -0,0 +1,9 @@ +<component name="CopyrightManager"> + <copyright> + <option name="notice" value="Copyright &#36;{YEAR} 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." /> + <option name="keyword" value="Copyright" /> + <option name="allowReplaceKeyword" value="" /> + <option name="myName" value="Apache 2.0" /> + <option name="myLocal" value="true" /> + </copyright> +</component>
\ No newline at end of file diff --git a/README.md b/README.md new file mode 100644 index 0000000..3aa5918 --- /dev/null +++ b/README.md @@ -0,0 +1,74 @@ +#JavaLang +This module provides marshalling, de-marshalling and handling of thread safe off heap memory through ByteBuffers. + +This module is available on maven central as + + <dependency> + <groupId>net.openhft</groupId> + <artifactId>lang</artifactId> + <version>6.1.1</version> + </dependency> + +The version 6.x signifies that it is build for Java 6+. (It requires Java 6 update 18 or later to build) + +## 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. + + public interface DataType { + // add getters and setters here + } + + // can create an array of any size (provided you have the memory) off heap. + HugeArray<DataType> array = HugeCollections.newArray(DataType.class, 10*1000*1000*1000L); + DataType dt = array.get(1111111111); + + // set data on dt + array.recycle(dt); // recycle the reference (or discard it) + + // create a ring buffer + HugeQueue<DataType> queue = HugeCollections.newQueue(DataType.class, 10*1000*1000L); + // give me a reference to an object to populate + DataType dt2 = queue.offer(); + // set the values od dt2 + queue.recycle(dt2); + + DataType dt3 = queue.take(); + // get values + queue.recycle(dt3); + +This is designed to be largely GC-less and you can queue millions of entries with 32 MB heap and not trigger GCs. + +## Working with buffers +To work with buffers there is a several options: +* _ByteBufferBytes_ which wraps [java.nio.ByteBuffer](http://docs.oracle.com/javase/7/docs/api/java/nio/ByteBuffer.html) +* _DirectBytes_ which is slices/records of [DirectStore](https://github.com/OpenHFT/Java-Lang/blob/master/lang/src/main/java/net/openhft/lang/io/DirectStore.java) - own implementation for offheap storage + +Both classes provide functionality: +* write\read operations for primitives (writeLong(long n), readLong() etc.) +* locking in native memory, so you can add thread safe constructs to your native record. +* CAS operations for int and long _boolean compareAndSwapInt(long offset, int expected, int x)_, _boolean compareAndSwapLong(long offset, long expected, long x)_ +* addAndGetInt and getAndAddInt operations + +####Example + ByteBuffer byteBuffer = ByteBuffer.allocate(SIZE).order(ByteOrder.nativeOrder()); + ByteBufferBytes bytes = new ByteBufferBytes(byteBuffer); + for (long i = 0; i < bytes.capacity(); i++) + bytes.writeLong(i); + for (long i = bytes.capacity()-8; i >= 0; i -= 8) { + int j = bytes.readLong(i); + assert i == j; + } + +#Building for eclipse + +Download Java-Lang zip from git https://github.com/OpenHFT/Java-Lang/archive/master.zip + +Unzip master.zip, Java-Lang-master folder will be extracted from zip. + + cd Java-Lang-master + mvn eclipse:eclipse + +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 new file mode 100755 index 0000000..a56b1a4 --- /dev/null +++ b/lang-integration/pom.xml @@ -0,0 +1,125 @@ +<?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-integration/src/test/java/net/openhft/lang/io/LockingViaMMapWithThreadIdMain.java b/lang-integration/src/test/java/net/openhft/lang/io/LockingViaMMapWithThreadIdMain.java new file mode 100644 index 0000000..23877c5 --- /dev/null +++ b/lang-integration/src/test/java/net/openhft/lang/io/LockingViaMMapWithThreadIdMain.java @@ -0,0 +1,104 @@ +/* + * 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.io; + +import net.openhft.affinity.AffinitySupport; + +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; + +/** + * 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 + * Toggled 10,000,128 times with an average delay of 14 ns on i7-3970X + */ +public class LockingViaMMapWithThreadIdMain { + static int RECORDS = Integer.getInteger("records", 256); + static int RECORD_SIZE = Integer.getInteger("record_size", 64); // double cache line size + static int WARMUP = Integer.getInteger("warmup", RECORDS * 50); + static int RUNS = Integer.getInteger("runs", 50 * 1000 * 1000); + + // offsets + static int LOCK = 0; + static int FLAG = 8; + static int LENGTH = 16; + + public static void main(String... args) throws IOException, InterruptedException { + boolean toggleTo = Boolean.parseBoolean(args[0]); + File tmpFile = new File(System.getProperty("java.io.tmpdir"), "lock-test-tid.dat"); + FileChannel fc = new RandomAccessFile(tmpFile, "rw").getChannel(); + 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(); + + long start = 0; + for (int i = -WARMUP / RECORDS; i < (RUNS + RECORDS - 1) / RECORDS; i++) { + if (i == 0) { + start = System.nanoTime(); + System.out.println("Started"); + } + + for (int j = 0; j < RECORDS; j++) { + int recordOffset = j * RECORD_SIZE; + for (int t = 1; t < 10000; t++) { + if (t == 0) + if (i >= 0) { + throw new AssertionError("Didn't toggle in time !??"); + } else { + Thread.sleep(200); + } + bytes.busyLockInt(recordOffset + LOCK); + try { + boolean flag = bytes.readBoolean(recordOffset + FLAG); + if (flag != toggleTo) { + bytes.writeBoolean(recordOffset + FLAG, toggleTo); + break; + } + } finally { + bytes.unlockInt(recordOffset + LOCK); + } + if (t % 100 == 0) + System.out.println("waiting for " + j + + " pid " + (bytes.readInt(recordOffset + LOCK) & (-1 >>> 8)) + + " is " + bytes.readBoolean(recordOffset + FLAG)); + if (t > 100) + if (t > 200) + Thread.sleep(1); + else + Thread.yield(); + } + } + } + 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); + fc.close(); + tmpFile.deleteOnExit(); + } +} diff --git a/lang-osgi/pom.xml b/lang-osgi/pom.xml new file mode 100755 index 0000000..fd8123f --- /dev/null +++ b/lang-osgi/pom.xml @@ -0,0 +1,215 @@ +<?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/main/java/net/openhft/langosgi/model/JavaBeanInterface.java b/lang-osgi/src/main/java/net/openhft/langosgi/model/JavaBeanInterface.java new file mode 100644 index 0000000..9675d65 --- /dev/null +++ b/lang-osgi/src/main/java/net/openhft/langosgi/model/JavaBeanInterface.java @@ -0,0 +1,68 @@ +/* + * 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.langosgi.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; + + void unlockRecord(); + + void setFlag(boolean flag); + + boolean getFlag(); + + void setByte(byte b); + + byte getByte(); + + void setShort(short s); + + short getShort(); + + void setChar(char ch); + + char getChar(); + + void setInt(int i); + + int getInt(); + + void setFloat(float f); + + float getFloat(); + + void setLong(long l); + + long getLong(); + + void setDouble(double d); + + double getDouble(); + + void setString(@MaxSize(8) String s); + + String getString(); +} 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 new file mode 100644 index 0000000..d734312 --- /dev/null +++ b/lang-osgi/src/test/java/net/openhft/lang/osgi/OSGiBundleTest.java @@ -0,0 +1,89 @@ +/* + * 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 new file mode 100644 index 0000000..6cae71a --- /dev/null +++ b/lang-osgi/src/test/java/net/openhft/lang/osgi/OSGiCollectionTest.java @@ -0,0 +1,74 @@ +/* + * 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/.gitignore b/lang/.gitignore new file mode 100644 index 0000000..7c73dad --- /dev/null +++ b/lang/.gitignore @@ -0,0 +1,3 @@ +*.ipr +*.iws +target/
\ No newline at end of file diff --git a/lang/pom.xml b/lang/pom.xml new file mode 100755 index 0000000..209a61a --- /dev/null +++ b/lang/pom.xml @@ -0,0 +1,175 @@ +<?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</artifactId> + <version>6.1.4-SNAPSHOT</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> + + <dependencies> + <dependency> + <groupId>org.kohsuke.jetbrains</groupId> + <artifactId>annotations</artifactId> + <version>9.0</version> + <scope>compile</scope> + </dependency> + + <dependency> + <groupId>net.openhft</groupId> + <artifactId>compiler</artifactId> + <version>2.1</version> + <optional>true</optional> + </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> + </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> + <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/src/main/java/net/openhft/lang/Compare.java b/lang/src/main/java/net/openhft/lang/Compare.java new file mode 100644 index 0000000..8d61054 --- /dev/null +++ b/lang/src/main/java/net/openhft/lang/Compare.java @@ -0,0 +1,176 @@ +/* + * 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; + +/** + * A generic Compare class for many types. It can be sub-classed so you can give types a different behaviour. + */ +public abstract class Compare { + + private static final long NULL_HASHCODE = Long.MIN_VALUE; + + public static boolean isEqual(boolean a, boolean b) { + return a == b; + } + + public static long calcLongHashCode(boolean a) { + return a ? 1 : 0; + } + + public static boolean isEqual(Boolean a, Boolean b) { + return a == null ? b == null : b != null && a.booleanValue() == b.booleanValue(); + } + + public static long calcLongHashCode(Boolean a) { + return a == null ? NULL_HASHCODE : calcLongHashCode(a.booleanValue()); + } + + public static boolean isEqual(byte a, byte b) { + return a == b; + } + + public static long calcLongHashCode(byte a) { + return a; + } + + public static boolean isEqual(Byte a, Byte b) { + return a == null ? b == null : b != null && a.byteValue() == b.byteValue(); + } + + public static long calcLongHashCode(Byte a) { + return a == null ? NULL_HASHCODE : calcLongHashCode(a.byteValue()); + } + + public static boolean isEqual(char a, char b) { + return a == b; + } + + public static long calcLongHashCode(char a) { + return a; + } + + public static boolean isEqual(Character a, Character b) { + return a == null ? b == null : b != null && a.charValue() == b.charValue(); + } + + public static long calcLongHashCode(Character a) { + return a == null ? NULL_HASHCODE : calcLongHashCode(a.charValue()); + } + + public static boolean isEqual(short a, short b) { + return a == b; + } + + public static long calcLongHashCode(short a) { + return a; + } + + public static boolean isEqual(Short a, Short b) { + return a == null ? b == null : b != null && a.shortValue() == b.shortValue(); + } + + public static long calcLongHashCode(Short a) { + return a == null ? NULL_HASHCODE : calcLongHashCode(a.shortValue()); + } + + public static boolean isEqual(int a, int b) { + return a == b; + } + + public static long calcLongHashCode(int a) { + return a; + } + + public static long calcLongHashCode(Integer a) { + return a == null ? NULL_HASHCODE : calcLongHashCode(a.intValue()); + } + + public static boolean isEqual(Integer a, Integer b) { + return a == null ? b == null : b != null && a.intValue() == b.intValue(); + } + + public static boolean isEqual(long a, long b) { + return a == b; + } + + public static long calcLongHashCode(long a) { + return a; + } + + public static boolean isEqual(Long a, Long b) { + return a == null ? b == null : b != null && a.longValue() == b.longValue(); + } + + public static long calcLongHashCode(Long a) { + return a == null ? NULL_HASHCODE : calcLongHashCode(a.longValue()); + } + + public static boolean isEqual(float a, float b) { + return Float.floatToRawIntBits(a) == Float.floatToRawIntBits(b); + } + + public static long calcLongHashCode(float a) { + return Float.floatToRawIntBits(a); + } + + public static boolean isEqual(Float a, Float b) { + return a == null ? b == null : b != null && isEqual(a.floatValue(), b.floatValue()); + } + + public static long calcLongHashCode(Float a) { + return a == null ? NULL_HASHCODE : calcLongHashCode(a.floatValue()); + } + + public static boolean isEqual(double a, double b) { + return Double.doubleToRawLongBits(a) == Double.doubleToRawLongBits(b); + } + + public static long calcLongHashCode(double a) { + return Double.doubleToRawLongBits(a); + } + + public static boolean isEqual(Double a, Double b) { + return a == null ? b == null : b != null && isEqual(a.doubleValue(), b.doubleValue()); + } + + public static long calcLongHashCode(Double a) { + return a == null ? NULL_HASHCODE : calcLongHashCode(a.doubleValue()); + } + + public static <T> boolean isEqual(T a, T b) { + return a == null ? b == null : b != null && a.equals(b); + } + + public static long calcLongHashCode(LongHashable t) { + return t.longHashCode(); + } + + public static long calcLongHashCode(Object t) { + return t == null ? NULL_HASHCODE : + t instanceof LongHashable ? calcLongHashCode((LongHashable) t) : + t instanceof CharSequence ? calcLongHashCode((CharSequence) t) : + t.hashCode(); + } + + public static long calcLongHashCode(CharSequence s) { + long hash = 0; + for (int i = 0, len = s.length(); i < len; i++) { + hash = 57 * hash + s.charAt(i); + } + return hash; + } +} diff --git a/lang/src/main/java/net/openhft/lang/Jvm.java b/lang/src/main/java/net/openhft/lang/Jvm.java new file mode 100755 index 0000000..99e07ab --- /dev/null +++ b/lang/src/main/java/net/openhft/lang/Jvm.java @@ -0,0 +1,90 @@ +/* + * 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; + +import java.io.File; +import java.io.IOException; +import java.lang.management.ManagementFactory; +import java.util.Random; +import java.util.logging.Logger; + +/** + * @author peter.lawrey + */ +public enum Jvm { + ; + public static final String TMP = System.getProperty("java.io.tmpdir"); + private static final boolean IS64BIT = is64Bit0(); + + public static boolean is64Bit() { + return IS64BIT; + } + + private static boolean is64Bit0() { + String systemProp; + systemProp = System.getProperty("com.ibm.vm.bitmode"); + if (systemProp != null) { + return "64".equals(systemProp); + } + systemProp = System.getProperty("sun.arch.data.model"); + if (systemProp != null) { + return "64".equals(systemProp); + } + systemProp = System.getProperty("java.vm.version"); + return systemProp != null && systemProp.contains("_64"); + } + + private static final int PROCESS_ID = getProcessId0(); + + public static int getProcessId() { + return PROCESS_ID; + } + + private static int getProcessId0() { + String pid = null; + final File self = new File("/proc/self"); + try { + if (self.exists()) + pid = self.getCanonicalFile().getName(); + } catch (IOException ignored) { + // ignored + } + if (pid == null) + pid = ManagementFactory.getRuntimeMXBean().getName().split("@")[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); + return rpid; + } else { + return Integer.parseInt(pid); + } + } + + /** + * This may or may not be the OS thread id, but should be unique across processes + * + * @return a unique tid of up to 48 bits. + */ + public static long getUniqueTid() { + return getUniqueTid(Thread.currentThread()); + } + + public static long getUniqueTid(Thread thread) { + return (long) getProcessId() << 32 | (thread.getId() & 0xFFFFFFFFL); + } + +} diff --git a/lang/src/main/java/net/openhft/lang/LongHashable.java b/lang/src/main/java/net/openhft/lang/LongHashable.java new file mode 100644 index 0000000..508ac7e --- /dev/null +++ b/lang/src/main/java/net/openhft/lang/LongHashable.java @@ -0,0 +1,26 @@ +/* + * 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; + +/** + * User: peter.lawrey + * Date: 08/10/13 + * Time: 12:44 + */ +public interface LongHashable { + long longHashCode(); +} diff --git a/lang/src/main/java/net/openhft/lang/Maths.java b/lang/src/main/java/net/openhft/lang/Maths.java new file mode 100755 index 0000000..f293677 --- /dev/null +++ b/lang/src/main/java/net/openhft/lang/Maths.java @@ -0,0 +1,154 @@ +/* + * 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; + +/** + * @author peter.lawrey + */ +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 long[] TENS = new long[19]; + + static { + TENS[0] = 1; + for (int i = 1; i < TENS.length; i++) + TENS[i] = TENS[i - 1] * 10; + } + + /** + * Performs a round which is accurate to within 1 ulp. i.e. for values very close to 0.5 it might be rounded up or + * down. This is a pragmatic choice for performance reasons as it is assumed you are not working on the edge of the + * precision of double. + * + * @param d value to round + */ + public static double round2(double d) { + final double factor = 1e2; + return d > WHOLE_NUMBER || d < -WHOLE_NUMBER ? d : + (long) (d < 0 ? d * factor - 0.5 : d * factor + 0.5) / factor; + } + + /** + * Performs a round which is accurate to within 1 ulp. i.e. for values very close to 0.5 it might be rounded up or + * down. This is a pragmatic choice for performance reasons as it is assumed you are not working on the edge of the + * precision of double. + * + * @param d value to round + */ + public static double round4(double d) { + final double factor = 1e4; + return d > Long.MAX_VALUE / factor || d < -Long.MAX_VALUE / factor ? d : + (long) (d < 0 ? d * factor - 0.5 : d * factor + 0.5) / factor; + } + + /** + * Performs a round which is accurate to within 1 ulp. i.e. for values very close to 0.5 it might be rounded up or + * down. This is a pragmatic choice for performance reasons as it is assumed you are not working on the edge of the + * precision of double. + * + * @param d value to round + */ + public static double round6(double d) { + final double factor = 1e6; + return d > Long.MAX_VALUE / factor || d < -Long.MAX_VALUE / factor ? d : + (long) (d < 0 ? d * factor - 0.5 : d * factor + 0.5) / factor; + } + + /** + * Performs a round which is accurate to within 1 ulp. i.e. for values very close to 0.5 it might be rounded up or + * down. This is a pragmatic choice for performance reasons as it is assumed you are not working on the edge of the + * precision of double. + * + * @param d value to round + */ + public static double round8(double d) { + final double factor = 1e8; + return d > Long.MAX_VALUE / factor || d < -Long.MAX_VALUE / factor ? d : + (long) (d < 0 ? d * factor - 0.5 : d * factor + 0.5) / factor; + } + + public static long power10(int n) { + if (n < 0 || n >= TENS.length) return -1; + return TENS[n]; + } + + public static int nextPower2(int n, int min) { + if (n < min) return min; + if ((n & (n - 1)) == 0) return n; + int i = min; + while (i < n) { + i *= 2; + if (i <= 0) return 1 << 30; + } + return i; + } + + public static long nextPower2(long n, long min) { + if (n < min) return min; + if ((n & (n - 1)) == 0) return n; + long i = min; + while (i < n) { + i *= 2; + if (i <= 0) return 1L << 62; + } + return i; + } + + + public static int hash(int n) { + 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(CharSequence cs) { + long hash = 0; + for (int i = 0; i < cs.length(); i++) + hash = hash * 131 + cs.charAt(i); + return hash; + } + + + /** + * Compares two {@code long} values numerically. The value returned is identical to what would be returned by: + * <pre> + * Long.valueOf(x).compareTo(Long.valueOf(y)) + * </pre> + * + * @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} + */ + public static int compare(long x, long y) { + return (x < y) ? -1 : ((x == y) ? 0 : 1); + } + + public static int intLog2(long num) { + long l = Double.doubleToRawLongBits(num); + return (int) ((l >> 52) - 1023); + } +} diff --git a/lang/src/main/java/net/openhft/lang/collection/HugeArray.java b/lang/src/main/java/net/openhft/lang/collection/HugeArray.java new file mode 100644 index 0000000..cfe1ffb --- /dev/null +++ b/lang/src/main/java/net/openhft/lang/collection/HugeArray.java @@ -0,0 +1,68 @@ +/* + * 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.collection; + +/** + * User: peter.lawrey + * Date: 08/10/13 + * Time: 07:40 + */ +public interface HugeArray<T> { + /** + * @return the capacity of the Array. + */ + long length(); + + /** + * Get a recycled object which is a reference to this element. + * + * @param index to look up. + * @return object reference to the element. + */ + T get(long index); + + /** + * Get a copy of the object in the array + * + * @param index of element to copy + * @param element Copyable element to copy to. + */ + void get(long index, T element); + + /** + * Copy the contents of an index to another object + * + * @param index to copy + * @param to object + */ + void copyTo(long index, T to); + + /** + * Set the data in the array to a copy of this element + * + * @param index to copy to + * @param t to copy + */ + void set(long index, T t); + + /** + * recycle the reference, + * + * @param t reference to recycle. + */ + void recycle(T t); +} diff --git a/lang/src/main/java/net/openhft/lang/collection/HugeCollections.java b/lang/src/main/java/net/openhft/lang/collection/HugeCollections.java new file mode 100644 index 0000000..4dbaad2 --- /dev/null +++ b/lang/src/main/java/net/openhft/lang/collection/HugeCollections.java @@ -0,0 +1,48 @@ +/* + * 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.collection; + +import net.openhft.lang.collection.impl.HugeArrayImpl; +import net.openhft.lang.collection.impl.HugeQueueImpl; +import net.openhft.lang.model.DataValueGenerator; + +/** + * User: peter.lawrey + * Date: 08/10/13 + * Time: 08:09 + */ +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); + } + + 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); + } +} diff --git a/lang/src/main/java/net/openhft/lang/collection/HugeQueue.java b/lang/src/main/java/net/openhft/lang/collection/HugeQueue.java new file mode 100644 index 0000000..c164fc4 --- /dev/null +++ b/lang/src/main/java/net/openhft/lang/collection/HugeQueue.java @@ -0,0 +1,69 @@ +/* + * 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.collection; + +/** + * User: peter.lawrey + * Date: 08/10/13 + * Time: 13:26 + */ +public interface HugeQueue<T> { + /** + * @return is full + */ + boolean isFull(); + + /** + * @return is empty + */ + boolean isEmpty(); + + /** + * Add to the end of a queue or return false if full. + * + * @return an element to populate or null if full + */ + T offer(); + + /** + * Add to the end of a queue or return false if full. + * + * @param element to add + * @return true if added or false if full. + */ + boolean offer(T element); + + /** + * @return the element or null is non is available + */ + T take(); + + /** + * Copy data to an element + * + * @param element to copy to + * @return true if one was available or false if not. + */ + boolean takeCopy(T element); + + /** + * Recycle an element reference. + * + * @param element reference to recycle. + */ + void recycle(T element); +} 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 new file mode 100644 index 0000000..97cf6dc --- /dev/null +++ b/lang/src/main/java/net/openhft/lang/collection/impl/HugeArrayImpl.java @@ -0,0 +1,111 @@ +/* + * 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.collection.impl; + +import net.openhft.lang.collection.HugeArray; +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 java.util.ArrayList; +import java.util.List; + +/** + * User: peter.lawrey Date: 08/10/13 Time: 08:11 + */ +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; + this.tClass = tClass; + this.length = length; + + T ref = valueGenerator.nativeInstance(tClass); + size = ((Byteable) ref).maxSize(); + store = new DirectStore(null, length * size); + ((Byteable) ref).bytes(store.createSlice()); + recycle(ref); + } + + private T createRef() { + T ref = valueGenerator.nativeInstance(tClass); + ((Byteable) ref).bytes(store.createSlice()); + return ref; + } + + @Override + public long length() { + return length; + } + + @Override + public T get(long index) { + T t = acquire(); + DirectBytes bytes = (DirectBytes) ((Byteable) t).bytes(); + bytes.positionAndSize(index * size, size); + return t; + } + + @Override + public void get(long index, T element) { + T t = acquire(); + DirectBytes bytes = (DirectBytes) ((Byteable) t).bytes(); + bytes.positionAndSize(index * size, size); + ((Copyable) element).copyFrom(t); + recycle(t); + } + + private T acquire() { + int size = freeList.size(); + if (size > 0) + return freeList.remove(size - 1); + return createRef(); + } + + @Override + public void copyTo(long index, T to) { + T from = get(index); + ((Copyable<T>) to).copyFrom(from); + recycle(from); + } + + @Override + public void set(long index, T from) { + T to = get(index); + ((Copyable<T>) to).copyFrom(from); + recycle(to); + } + + @Override + public void recycle(T t) { + if (freeList.size() < MAX_SIZE) { + assert ((DirectBytes) ((Byteable) t).bytes()).store() == store; + assert !freeList.contains(t) : "recycling object already recycled"; + freeList.add(t); + } + } +} 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 new file mode 100644 index 0000000..620efc9 --- /dev/null +++ b/lang/src/main/java/net/openhft/lang/collection/impl/HugeQueueImpl.java @@ -0,0 +1,89 @@ +/* + * 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.collection.impl; + +import net.openhft.lang.collection.HugeArray; +import net.openhft.lang.collection.HugeQueue; + +/** + * User: peter.lawrey + * Date: 08/10/13 + * Time: 13:31 + */ +public class HugeQueueImpl<T> implements HugeQueue<T> { + private final HugeArray<T> array; + private final long size; + private long start, end; + + public HugeQueueImpl(HugeArray<T> tHugeArray, long size) { + array = tHugeArray; + this.size = size; + } + + public boolean isFull() { + return next(end) == start; + } + + private long next(long end) { + return (end + 1) % size; + } + + public boolean isEmpty() { + return end == start; + } + + @Override + public boolean offer(T element) { + if (isFull()) + return false; + array.set(end % size, element); + end = next(end); + return true; + } + + @Override + public T take() { + if (isEmpty()) + return null; + long pos = start % size; + start = next(start); + return array.get(pos); + } + + @Override + public boolean takeCopy(T element) { + if (isEmpty()) + return false; + array.get(start % size, element); + start = next(start); + return true; + } + + @Override + public T offer() { + if (isFull()) + return null; + T t = array.get(end % size); + end = next(end); + return t; + } + + @Override + public void recycle(T element) { + array.recycle(element); + } +} diff --git a/lang/src/main/java/net/openhft/lang/io/AbstractBytes.java b/lang/src/main/java/net/openhft/lang/io/AbstractBytes.java new file mode 100755 index 0000000..e167835 --- /dev/null +++ b/lang/src/main/java/net/openhft/lang/io/AbstractBytes.java @@ -0,0 +1,2323 @@ +/* + * 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.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.BytesMarshallerFactory; +import net.openhft.lang.io.serialization.CompactBytesMarshaller; +import net.openhft.lang.io.serialization.impl.NoMarshaller; +import net.openhft.lang.io.serialization.impl.VanillaBytesMarshallerFactory; +import net.openhft.lang.pool.StringInterner; +import org.jetbrains.annotations.NotNull; +import org.jetbrains.annotations.Nullable; + +import java.io.*; +import java.math.BigInteger; +import java.nio.ByteBuffer; +import java.nio.ByteOrder; +import java.nio.charset.Charset; +import java.text.SimpleDateFormat; +import java.util.*; +import java.util.logging.Level; +import java.util.logging.Logger; + +/** + * @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 long UNSIGNED_INT_MASK = 0xFFFFFFFFL; + // 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 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(); + private static final byte[] NaN = "NaN".getBytes(); + private static final long MAX_VALUE_DIVIDE_5 = Long.MAX_VALUE / 5; + private static final byte BYTE_MIN_VALUE = Byte.MIN_VALUE; + private static final byte BYTE_EXTENDED = Byte.MIN_VALUE + 1; + private static final byte BYTE_MAX_VALUE = Byte.MIN_VALUE + 2; + private static final short UBYTE_EXTENDED = 0xff; + 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 final byte[] numberBuffer = new byte[MAX_NUMBER_LENGTH]; + protected boolean finished; + protected BytesMarshallerFactory bytesMarshallerFactory; + 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; + + protected AbstractBytes() { + this(new VanillaBytesMarshallerFactory()); + } + + protected AbstractBytes(BytesMarshallerFactory bytesMarshallerFactory) { + this.finished = false; + this.bytesMarshallerFactory = bytesMarshallerFactory; + } + + static boolean equalsCaseIgnore(StringBuilder sb, String s) { + if (sb.length() != s.length()) + return false; + for (int i = 0; i < s.length(); i++) + if (Character.toLowerCase(sb.charAt(i)) != s.charAt(i)) + return false; + return true; + } + + private static double asDouble(long value, int exp, boolean negative, int decimalPlaces) { + if (decimalPlaces > 0 && value < Long.MAX_VALUE / 2) { + if (value < Long.MAX_VALUE / (1L << 32)) { + exp -= 32; + value <<= 32; + } + if (value < Long.MAX_VALUE / (1L << 16)) { + exp -= 16; + value <<= 16; + } + if (value < Long.MAX_VALUE / (1L << 8)) { + exp -= 8; + value <<= 8; + } + if (value < Long.MAX_VALUE / (1L << 4)) { + exp -= 4; + value <<= 4; + } + if (value < Long.MAX_VALUE / (1L << 2)) { + exp -= 2; + value <<= 2; + } + if (value < Long.MAX_VALUE / (1L << 1)) { + exp -= 1; + value <<= 1; + } + } + for (; decimalPlaces > 0; decimalPlaces--) { + exp--; + long mod = value % 5; + value /= 5; + int modDiv = 1; + if (value < Long.MAX_VALUE / (1L << 4)) { + exp -= 4; + value <<= 4; + modDiv <<= 4; + } + if (value < Long.MAX_VALUE / (1L << 2)) { + exp -= 2; + value <<= 2; + modDiv <<= 2; + } + if (value < Long.MAX_VALUE / (1L << 1)) { + exp -= 1; + value <<= 1; + modDiv <<= 1; + } + if (decimalPlaces > 1) + value += modDiv * mod / 5; + else + value += (modDiv * mod + 4) / 5; + } + final double d = Math.scalb((double) value, exp); + return negative ? -d : d; + } + + private static void warnIdLimit(long id) { + LOGGER.log(Level.WARNING, "High thread id may result in collisions id: " + id); + + ID_LIMIT_WARNED = true; + } + + protected StringInterner stringInterner() { + if (stringInterner == null) + stringInterner = new StringInterner(8 * 1024); + return stringInterner; + } + + @Override + public Boolean parseBoolean(@NotNull StopCharTester tester) { + StringBuilder sb = acquireUtfReader(); + parseUTF(sb, tester); + if (sb.length() == 0) + return null; + switch (sb.charAt(0)) { + case 't': + case 'T': + return sb.length() == 1 || equalsCaseIgnore(sb, "true") ? true : null; + case 'y': + case 'Y': + return sb.length() == 1 || equalsCaseIgnore(sb, "yes") ? true : null; + case '0': + return sb.length() == 1 ? false : null; + case '1': + return sb.length() == 1 ? true : null; + case 'f': + case 'F': + return sb.length() == 1 || equalsCaseIgnore(sb, "false") ? false : null; + case 'n': + case 'N': + return sb.length() == 1 || equalsCaseIgnore(sb, "no") ? false : null; + } + return null; + } + + @Override + public void readFully(@NotNull byte[] bytes) { + readFully(bytes, 0, bytes.length); + } + + @Override + public int skipBytes(int n) { + long position = position(); + int n2 = (int) Math.min(n, capacity() - position); + position(position + n2); + return n2; + } + + @Override + public boolean readBoolean() { + return readByte() != 0; + } + + @Override + public boolean readBoolean(long offset) { + return readByte(offset) != 0; + } + + @Override + public int readUnsignedByte() { + return readByte() & UNSIGNED_BYTE_MASK; + } + + @Override + public int readUnsignedByte(long offset) { + return readByte(offset) & UNSIGNED_BYTE_MASK; + } + + @Override + public int readUnsignedShort() { + return readShort() & UNSIGNED_SHORT_MASK; + } + + @Override + public int readUnsignedShort(long offset) { + return readShort(offset) & UNSIGNED_SHORT_MASK; + } + + @NotNull + @Override + public String readLine() { + StringBuilder input = acquireUtfReader(); + EOL: + while (position() < capacity()) { + int c = readUnsignedByte(); + switch (c) { + case '\n': + break EOL; + case '\r': + long cur = position(); + if (cur < capacity() && readByte(cur) == '\n') + position(cur + 1); + break EOL; + default: + input.append((char) c); + break; + } + } + return stringInterner().intern(input); + } + + @Nullable + @Override + public String readUTFΔ() { + if (readUTFΔ(acquireUtfReader())) + return utfReader.length() == 0 ? "" : stringInterner().intern(utfReader); + return null; + } + + @Nullable + @Override + public String readUTFΔ(long offset) throws IllegalStateException { + long position = position(); + try { + position(offset); + return readUTFΔ(); + } finally { + position(position); + } + } + + // RandomDataOutput + + @NotNull + private StringBuilder acquireUtfReader() { + if (utfReader == null) + utfReader = new StringBuilder(); + else + utfReader.setLength(0); + return utfReader; + } + + @Override + public boolean readUTFΔ(@NotNull StringBuilder stringBuilder) { + try { + stringBuilder.setLength(0); + return appendUTF0(stringBuilder); + } catch (IOException unexpected) { + throw new IllegalStateException(unexpected); + } + } + + @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; + 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); + return stringInterner().intern(utfReader); + } + + @Override + public void parseUTF(@NotNull StringBuilder builder, @NotNull StopCharTester tester) { + builder.setLength(0); + try { + readUTF0(builder, tester); + } catch (IOException e) { + throw new AssertionError(e); + } + } + + private void readUTF0(@NotNull Appendable appendable, @NotNull StopCharTester tester) throws IOException { + while (remaining() > 0) { + int c = readByte(); + if (c < 0) { + position(position() - 1); + break; + } + if (tester.isStopChar(c)) + return; + appendable.append((char) c); + } + + while (remaining() > 0) { + int c = readUnsignedByte(); + switch (c >> 4) { + case 0: + case 1: + case 2: + case 3: + case 4: + case 5: + case 6: + case 7: + /* 0xxxxxxx */ + if (tester.isStopChar(c)) + return; + appendable.append((char) c); + break; + case 12: + case 13: { + /* 110x xxxx 10xx xxxx */ + int char2 = readUnsignedByte(); + if ((char2 & 0xC0) != 0x80) + throw new UTFDataFormatException( + "malformed input around byte"); + int c2 = (char) (((c & 0x1F) << 6) | + (char2 & 0x3F)); + if (tester.isStopChar(c2)) + return; + appendable.append((char) c2); + break; + } + case 14: { + /* 1110 xxxx 10xx xxxx 10xx xxxx */ + + int char2 = readUnsignedByte(); + int char3 = readUnsignedByte(); + + if (((char2 & 0xC0) != 0x80) || ((char3 & 0xC0) != 0x80)) + throw new UTFDataFormatException( + "malformed input around byte "); + int c3 = (char) (((c & 0x0F) << 12) | + ((char2 & 0x3F) << 6) | + (char3 & 0x3F)); + if (tester.isStopChar(c3)) + return; + appendable.append((char) c3); + break; + } + default: + /* 10xx xxxx, 1111 xxxx */ + throw new UTFDataFormatException( + "malformed input around byte "); + } + } + } + + @Override + public boolean stepBackAndSkipTo(@NotNull StopCharTester tester) { + if (position() > 0) + position(position() - 1); + return skipTo(tester); + } + + @Override + public boolean skipTo(@NotNull StopCharTester tester) { + while (remaining() > 0) { + int ch = readByte(); + if (tester.isStopChar(ch)) + return true; + } + return false; + } + + @NotNull + @Override + public String readUTF() { + try { + int len = readUnsignedShort(); + readUTF0(acquireUtfReader(), len); + return utfReader.length() == 0 ? "" : stringInterner().intern(utfReader); + } catch (IOException unexpected) { + throw new AssertionError(unexpected); + } + } + + @Override + public short readCompactShort() { + byte b = readByte(); + switch (b) { + case BYTE_MIN_VALUE: + return Short.MIN_VALUE; + case BYTE_MAX_VALUE: + return Short.MAX_VALUE; + case BYTE_EXTENDED: + return readShort(); + default: + return b; + } + } + + @Override + public int readCompactUnsignedShort() { + int b = readUnsignedByte(); + if (b == UBYTE_EXTENDED) + return readUnsignedShort(); + return b; + } + + @Override + public int readInt24() { + int b = readUnsignedByte(); + int s = readUnsignedShort(); + if (byteOrder() == ByteOrder.BIG_ENDIAN) + return ((b << 24) + (s << 8)) >> 8; + // extra shifting to get sign extension. + return ((b << 8) + (s << 16)) >> 8; + } + + @Override + public int readInt24(long offset) { + int b = readUnsignedByte(offset); + int s = readUnsignedShort(offset + 1); + if (byteOrder() == ByteOrder.BIG_ENDIAN) + return ((b << 24) + (s << 8)) >> 8; + // extra shifting to get sign extension. + return ((b << 8) + (s << 16)) >> 8; + } + + @Override + public long readUnsignedInt() { + return readInt() & UNSIGNED_INT_MASK; + } + + @Override + public long readUnsignedInt(long offset) { + return readInt(offset) & UNSIGNED_INT_MASK; + } + + @Override + public int readCompactInt() { + short b = readShort(); + switch (b) { + case SHORT_MIN_VALUE: + return Integer.MIN_VALUE; + case SHORT_MAX_VALUE: + return Integer.MAX_VALUE; + case SHORT_EXTENDED: + return readInt(); + default: + return b; + } + } + + @Override + public long readCompactUnsignedInt() { + int b = readUnsignedShort(); + if (b == USHORT_EXTENDED) + return readUnsignedInt(); + return b; + } + + @Override + public long readInt48() { + long s = readUnsignedShort(); + long l = readUnsignedInt(); + if (byteOrder() == ByteOrder.BIG_ENDIAN) + return ((s << 48) + (l << 16)) >> 16; + // extra shifting to get sign extension. + return ((s << 16) + (l << 32)) >> 16; + } + + @Override + public long readInt48(long offset) { + long s = readUnsignedShort(offset); + long l = readUnsignedInt(offset + 2); + if (byteOrder() == ByteOrder.BIG_ENDIAN) + return ((s << 48) + (l << 16)) >> 16; + // extra shifting to get sign extension. + return ((s << 16) + (l << 32)) >> 16; + } + + @Override + public long readCompactLong() { + int b = readInt(); + switch (b) { + case INT_MIN_VALUE: + return Long.MIN_VALUE; + case INT_MAX_VALUE: + return Long.MAX_VALUE; + case INT_EXTENDED: + return readLong(); + default: + return b; + } + } + + @Override + public long readStopBit() { + long l = 0, b; + int count = 0; + while ((b = readByte()) < 0) { + l |= (b & 0x7FL) << count; + count += 7; + } + if (b == 0 && count > 0) + return ~l; + return l | (b << count); + } + + @Override + public double readCompactDouble() { + float f = readFloat(); + if (Float.isNaN(f)) + return readDouble(); + return f; + } + + @Override + public void read(@NotNull ByteBuffer bb) { + int len = (int) Math.min(bb.remaining(), remaining()); + if (bb.order() == byteOrder()) { + while (len >= 8) { + bb.putLong(readLong()); + len -= 8; + } + } + while (len > 0) { + bb.put(readByte()); + len--; + } + } + + // // RandomOutputStream + @Override + public void write(@NotNull byte[] bytes) { + int length = bytes.length; + checkWrite(length); + write(bytes, 0, length); + } + + private void checkWrite(int length) { + if (length > remaining()) + throw new IllegalStateException("Cannot write " + length + " only " + remaining() + " remaining"); + } + + @Override + public void writeBoolean(boolean v) { + write(v ? -1 : 0); + } + + @Override + public void writeBoolean(long offset, boolean v) { + writeByte(offset, v ? -1 : 0); + } + + @Override + public void writeBytes(@NotNull String s) { + int len = s.length(); + for (int i = 0; i < len; i++) + write(s.charAt(i)); + } + + @Override + public void writeChars(@NotNull String s) { + int len = s.length(); + for (int i = 0; i < len; i++) + writeChar(s.charAt(i)); + } + + @Override + public void writeUTF(@NotNull String str) { + long strlen = str.length(); + long utflen = findUTFLength(str, strlen); + if (utflen > 65535) + throw new IllegalStateException("String too long " + utflen + " when encoded, max: 65535"); + writeUnsignedShort((int) utflen); + writeUTF0(str, strlen); + } + + @Override + public void writeUTFΔ(@Nullable CharSequence str) { + if (str == null) { + writeStopBit(-1); + return; + } + long strlen = str.length(); + long utflen = findUTFLength(str, strlen); + writeStopBit(utflen); + writeUTF0(str, strlen); + } + + @Override + public 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); + writeStopBit(utflen); + writeUTF0(s, strlen); + } finally { + position(position); + } + } + + @NotNull + public ByteStringAppender append(@NotNull CharSequence str) { + if (str == null) + return this; + long strlen = str.length(); + writeUTF0(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; + } + } + + 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 + public void writeByte(int v) { + write(v); + } + + @Override + public void writeUnsignedByte(int v) { + writeByte(v); + } + + @Override + public void writeUnsignedByte(long offset, int v) { + writeByte(offset, v); + } + + @Override + public void write(long offset, @NotNull byte[] bytes) { + checkWrite(bytes.length); + for (int i = 0; i < bytes.length; i++) + writeByte(offset + i, bytes[i]); + } + + @Override + public void write(byte[] bytes, int off, int len) { + checkWrite(bytes.length); + + for (int i = 0; i < len; i++) + write(bytes[off + i]); + } + + @Override + public void writeUnsignedShort(int v) { + writeShort(v); + } + + @Override + public void writeUnsignedShort(long offset, int v) { + writeShort(offset, v); + } + + @Override + public void writeCompactShort(int v) { + if (v > BYTE_MAX_VALUE && v <= Byte.MAX_VALUE) + writeByte(v); + else + switch (v) { + case Short.MIN_VALUE: + writeByte(BYTE_MIN_VALUE); + break; + case Short.MAX_VALUE: + writeByte(BYTE_MAX_VALUE); + break; + default: + writeByte(BYTE_EXTENDED); + writeShort(v); + break; + } + } + + @Override + public void writeCompactUnsignedShort(int v) { + if (v >= 0 && v < USHORT_EXTENDED) { + writeByte(v); + } else { + writeUnsignedShort(USHORT_EXTENDED); + writeUnsignedShort(v); + } + } + + @Override + public void writeInt24(int v) { + if (byteOrder() == ByteOrder.BIG_ENDIAN) { + writeUnsignedByte(v >>> 16); + writeUnsignedShort(v); + } else { + writeUnsignedByte(v); + writeUnsignedShort(v >>> 8); + } + } + + @Override + public void writeInt24(long offset, int v) { + if (byteOrder() == ByteOrder.BIG_ENDIAN) { + writeUnsignedByte(offset, v >>> 16); + writeUnsignedShort(offset + 1, v); + } else { + writeUnsignedByte(offset, v); + writeUnsignedShort(offset + 1, v >>> 8); + } + } + + @Override + public void writeUnsignedInt(long v) { + writeInt((int) v); + } + + @Override + public void writeUnsignedInt(long offset, long v) { + writeInt(offset, (int) v); + } + + @Override + public void writeCompactInt(int v) { + if (v > SHORT_MAX_VALUE && v <= Short.MAX_VALUE) + writeShort(v); + else + switch (v) { + case Integer.MIN_VALUE: + writeShort(SHORT_MIN_VALUE); + break; + case Integer.MAX_VALUE: + writeShort(SHORT_MAX_VALUE); + break; + default: + writeShort(SHORT_EXTENDED); + writeInt(v); + break; + } + } + + @Override + public void writeCompactUnsignedInt(long v) { + if (v >= 0 && v < USHORT_EXTENDED) { + writeShort((int) v); + } else { + writeShort(USHORT_EXTENDED); + writeUnsignedInt(v); + } + } + + @Override + public void writeInt48(long v) { + if (byteOrder() == ByteOrder.BIG_ENDIAN) { + writeUnsignedShort((int) (v >>> 32)); + writeUnsignedInt(v); + } else { + writeUnsignedShort((int) v); + writeUnsignedInt(v >>> 16); + } + } + + @Override + public void writeInt48(long offset, long v) { + if (byteOrder() == ByteOrder.BIG_ENDIAN) { + writeUnsignedShort(offset, (int) (v >>> 32)); + writeUnsignedInt(offset + 2, v); + } else { + writeUnsignedShort(offset, (int) v); + writeUnsignedInt(offset + 2, v >>> 16); + } + } + + @Override + public void writeCompactLong(long v) { + if (v > INT_MAX_VALUE && v <= Integer.MAX_VALUE) { + writeInt((int) v); + + } else if (v == Long.MIN_VALUE) { + writeInt(INT_MIN_VALUE); + + } else if (v == Long.MAX_VALUE) { + writeInt(INT_MAX_VALUE); + + } else { + writeInt(INT_EXTENDED); + writeLong(v); + + } + } + + @Override + public void writeStopBit(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; + } + } + } + + @Override + public void writeCompactDouble(double v) { + float f = (float) v; + if (f == v) { + writeFloat(f); + } else { + writeFloat(Float.NaN); + writeDouble(v); + } + } + + @Override + public void write(@NotNull ByteBuffer bb) { + if (bb.order() == byteOrder()) + while (bb.remaining() >= 8) + writeLong(bb.getLong()); + while (bb.remaining() >= 1) + 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 + @NotNull + @Override + public ByteStringAppender append(@NotNull CharSequence s, int start, int end) { + for (int i = start, len = Math.min(end, s.length()); i < len; i++) + writeByte(s.charAt(i)); + return this; + } + + @NotNull + @Override + public ByteStringAppender append(@Nullable Enum value) { + return value == null ? this : append(value.toString()); + } + + @NotNull + @Override + public ByteStringAppender append(boolean b) { + append(b ? "true" : "false"); + return this; + } + + @NotNull + @Override + public ByteStringAppender append(char c) { + writeByte(c); + return this; + } + + @NotNull + @Override + public ByteStringAppender append(int num) { + return append((long) num); + } + + @NotNull + @Override + public ByteStringAppender append(long num) { + if (num < 0) { + if (num == Long.MIN_VALUE) { + write(MIN_VALUE_TEXT); + return this; + } + writeByte('-'); + num = -num; + } + if (num == 0) { + writeByte('0'); + + } else { + appendLong0(num); + } + return this; + } + + @NotNull + @Override + public ByteStringAppender append(long num, int base) { + if (base < 2 || base > Character.MAX_RADIX) + throw new IllegalArgumentException("Invalid base: " + base); + if (num < 0) { + if (num == Long.MIN_VALUE) { + writeBytes(BigInteger.valueOf(num).toString(base)); + return this; + } + writeByte('-'); + num = -num; + } + if (num == 0) { + writeByte('0'); + + } else { + while (num > 0) { + writeByte(RADIX[((int) (num % base))]); + num /= base; + } + } + return this; + } + + @NotNull + @Override + public ByteStringAppender appendDateMillis(long timeInMS) { + if (dateFormat == null) { + dateFormat = new SimpleDateFormat("yyyy/MM/dd"); + dateFormat.setTimeZone(TimeZone.getTimeZone("GMT")); + } + long date = timeInMS / 86400000; + if (lastDay != date) { + lastDateStr = dateFormat.format(new Date(timeInMS)).getBytes(ISO_8859_1); + lastDay = date; + } else { + assert lastDateStr != null; + } + write(lastDateStr); + return this; + } + + @NotNull + @Override + public ByteStringAppender appendDateTimeMillis(long timeInMS) { + appendDateMillis(timeInMS); + writeByte('T'); + appendTimeMillis(timeInMS % 86400000L); + return this; + } + + @NotNull + @Override + public ByteStringAppender appendTimeMillis(long timeInMS) { + int hours = (int) (timeInMS / (60 * 60 * 1000)); + if (hours > 99) { + appendLong0(hours); // can have over 24 hours. + } else { + writeByte((char) (hours / 10 + '0')); + writeByte((char) (hours % 10 + '0')); + } + writeByte(':'); + int minutes = (int) ((timeInMS / (60 * 1000)) % 60); + writeByte((char) (minutes / 10 + '0')); + writeByte((char) (minutes % 10 + '0')); + writeByte(':'); + int seconds = (int) ((timeInMS / 1000) % 60); + writeByte((char) (seconds / 10 + '0')); + writeByte((char) (seconds % 10 + '0')); + writeByte('.'); + int millis = (int) (timeInMS % 1000); + writeByte((char) (millis / 100 + '0')); + writeByte((char) (millis / 10 % 10 + '0')); + writeByte((char) (millis % 10 + '0')); + return this; + } + + @NotNull + @Override + public ByteStringAppender append(double d) { + long val = Double.doubleToRawLongBits(d); + int sign = (int) (val >>> 63); + int exp = (int) ((val >>> 52) & 2047); + long mantissa = val & ((1L << 52) - 1); + if (sign != 0) { + writeByte('-'); + } + 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; + } + final int shift = (1023 + 52) - exp; + if (shift > 0) { + // integer and faction + if (shift < 53) { + long intValue = mantissa >> shift; + appendLong0(intValue); + mantissa -= intValue << shift; + if (mantissa > 0) { + writeByte('.'); + mantissa <<= 1; + mantissa++; + int precision = shift + 1; + long error = 1; + + long value = intValue; + int decimalPlaces = 0; + while (mantissa > error) { + // times 5*2 = 10 + mantissa *= 5; + error *= 5; + precision--; + long num = (mantissa >> precision); + value = value * 10 + num; + writeByte((char) ('0' + num)); + mantissa -= num << precision; + + final double parsedValue = asDouble(value, 0, sign != 0, ++decimalPlaces); + if (parsedValue == d) + break; + } + } + return this; + + } else { + // faction. + writeByte('0'); + writeByte('.'); + mantissa <<= 6; + mantissa += (1 << 5); + int precision = shift + 6; + + long error = (1 << 5); + + long value = 0; + int decimalPlaces = 0; + while (mantissa > error) { + while (mantissa > MAX_VALUE_DIVIDE_5) { + mantissa >>>= 1; + error = (error + 1) >>> 1; + precision--; + } + // times 5*2 = 10 + mantissa *= 5; + error *= 5; + precision--; + if (precision >= 64) { + decimalPlaces++; + writeByte('0'); + continue; + } + long num = (mantissa >>> precision); + value = value * 10 + num; + final char c = (char) ('0' + num); + assert !(c < '0' || c > '9'); + writeByte(c); + mantissa -= num << precision; + final double parsedValue = asDouble(value, 0, sign != 0, ++decimalPlaces); + if (parsedValue == d) + break; + } + return this; + } + } + // large number + mantissa <<= 10; + int precision = -10 - shift; + int digits = 0; + while ((precision > 53 || mantissa > Long.MAX_VALUE >> precision) && precision > 0) { + digits++; + precision--; + long mod = mantissa % 5; + mantissa /= 5; + int modDiv = 1; + while (mantissa < MAX_VALUE_DIVIDE_5 && precision > 1) { + precision -= 1; + mantissa <<= 1; + modDiv <<= 1; + } + mantissa += modDiv * mod / 5; + } + long val2 = precision > 0 ? mantissa << precision : mantissa >>> -precision; + + appendLong0(val2); + for (int i = 0; i < digits; i++) + writeByte('0'); + + return this; + } + + @Override + public double parseDouble() { + long value = 0; + int exp = 0; + boolean negative = false; + int decimalPlaces = Integer.MIN_VALUE; + while (true) { + byte ch = readByte(); + if (ch >= '0' && ch <= '9') { + while (value >= MAX_VALUE_DIVIDE_10) { + value >>>= 1; + exp++; + } + value = value * 10 + (ch - '0'); + decimalPlaces++; + } else if (ch == '-') { + negative = true; + } else if (ch == '.') { + decimalPlaces = 0; + } else { + break; + } + } + + return asDouble(value, exp, negative, decimalPlaces); + } + + @NotNull + @Override + public <E> ByteStringAppender append(@NotNull Iterable<E> list, @NotNull CharSequence separator) { + if (list instanceof RandomAccess && list instanceof List) { + return append((List<E>) list, separator); + } + int i = 0; + for (E e : list) { + if (i++ > 0) + append(separator); + if (e != null) + append(e.toString()); + } + return this; + } + + @NotNull + public <E> ByteStringAppender append(@NotNull List<E> list, @NotNull CharSequence separator) { + for (int i = 0; i < list.size(); i++) { + if (i > 0) + append(separator); + E e = list.get(i); + if (e != null) + append(e.toString()); + } + return this; + } + + @NotNull + @Override + public MutableDecimal parseDecimal(@NotNull MutableDecimal decimal) { + long num = 0, scale = Long.MIN_VALUE; + boolean negative = false; + while (true) { + byte b = readByte(); + // 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; + } + } + if (negative) + num = -num; + decimal.set(num, scale > 0 ? (int) scale : 0); + return decimal; + } + + @Override + public long parseLong() { + long num = 0; + boolean negative = false; + while (true) { + byte b = readByte(); + // if (b >= '0' && b <= '9') + if ((b - ('0' + Integer.MIN_VALUE)) <= 9 + Integer.MIN_VALUE) + num = num * 10 + b - '0'; + else if (b == '-') + negative = true; + else + break; + } + return negative ? -num : num; + } + + @Override + public long parseLong(int base) { + if (base < 2 || base > Character.MAX_RADIX) + throw new IllegalArgumentException("Invalid base:" + base); + long num = 0; + boolean negative = false; + while (true) { + byte b = readByte(); + byte rp = RADIX_PARSE[b]; + if (rp >= 0 && rp < base) { + num = num * base + rp; + } else if (b == '-') + negative = true; + else + break; + } + return negative ? -num : num; + } + + private void appendLong0(long num) { + // Extract digits into the end of the numberBuffer + int endIndex = appendLong1(num); + + // Bulk copy the digits into the front of the buffer + write(numberBuffer, endIndex, MAX_NUMBER_LENGTH - endIndex); + } + + private int appendLong1(long num) { + numberBuffer[19] = (byte) (num % 10L + '0'); + num /= 10; + if (num <= 0) + return 19; + numberBuffer[18] = (byte) (num % 10L + '0'); + num /= 10; + if (num <= 0) + return 18; + numberBuffer[17] = (byte) (num % 10L + '0'); + num /= 10; + if (num <= 0) + return 17; + numberBuffer[16] = (byte) (num % 10L + '0'); + num /= 10; + if (num <= 0) + return 16; + numberBuffer[15] = (byte) (num % 10L + '0'); + num /= 10; + if (num <= 0) + return 15; + numberBuffer[14] = (byte) (num % 10L + '0'); + num /= 10; + if (num <= 0) + return 14; + numberBuffer[13] = (byte) (num % 10L + '0'); + num /= 10; + if (num <= 0) + return 13; + numberBuffer[12] = (byte) (num % 10L + '0'); + num /= 10; + if (num <= 0) + return 12; + numberBuffer[11] = (byte) (num % 10L + '0'); + num /= 10; + if (num <= 0) + return 11; + numberBuffer[10] = (byte) (num % 10L + '0'); + num /= 10; + if (num <= 0) + return 10; + numberBuffer[9] = (byte) (num % 10L + '0'); + num /= 10; + if (num <= 0) + return 9; + numberBuffer[8] = (byte) (num % 10L + '0'); + num /= 10; + if (num <= 0) + return 8; + numberBuffer[7] = (byte) (num % 10L + '0'); + num /= 10; + if (num <= 0) + return 7; + numberBuffer[6] = (byte) (num % 10L + '0'); + num /= 10; + if (num <= 0) + return 6; + numberBuffer[5] = (byte) (num % 10L + '0'); + num /= 10; + if (num <= 0) + return 5; + numberBuffer[4] = (byte) (num % 10L + '0'); + num /= 10; + if (num <= 0) + return 4; + numberBuffer[3] = (byte) (num % 10L + '0'); + num /= 10; + if (num <= 0) + return 3; + numberBuffer[2] = (byte) (num % 10L + '0'); + num /= 10; + if (num <= 0) + return 2; + numberBuffer[1] = (byte) (num % 10L + '0'); + num /= 10; + return 1; + } + + @NotNull + @Override + public ByteStringAppender append(double d, int precision) { + if (precision < 0) + precision = 0; + long power10 = Maths.power10(precision); + if (power10 < 0) + power10 = 100000000000000000L; + if (d < 0) { + d = -d; + writeByte('-'); + } + double d2 = d * power10; + if (d2 > Long.MAX_VALUE || d2 < Long.MIN_VALUE + 1) + return append(d); + long val = (long) (d2 + 0.5); + while (precision > 1 && val % 10 == 0) { + val /= 10; + precision--; + } + if (precision > 0 && val % 10 == 0) { + val = (val + 5) / 10; + precision--; + } + if (precision > 0) + appendDouble0(val, precision); + else + appendLong0(val); + return this; + } + + private void appendDouble0(long num, int precision) { + // Extract digits into the end of the numberBuffer + // Once desired precision is reached, write the '.' + int endIndex = appendDouble1(num, precision); + + // Bulk copy the digits into the front of the buffer + // TODO: Can this be avoided with use of correctly offset bulk appends on Excerpt? + // Uses (numberBufferIdx - 1) because index was advanced one too many times + + write(numberBuffer, endIndex, MAX_NUMBER_LENGTH - endIndex); + } + + private int appendDouble1(long num, int precision) { + int endIndex = MAX_NUMBER_LENGTH; + int maxEnd = MAX_NUMBER_LENGTH - precision - 2; + numberBuffer[--endIndex] = (byte) (num % 10L + '0'); + num /= 10; + if (num <= 0 && endIndex <= maxEnd) + return endIndex; + if (precision == 1) + numberBuffer[--endIndex] = (byte) '.'; + numberBuffer[--endIndex] = (byte) (num % 10L + '0'); + num /= 10; + if (num <= 0 && endIndex <= maxEnd) + return endIndex; + if (precision == 2) + numberBuffer[--endIndex] = (byte) '.'; + numberBuffer[--endIndex] = (byte) (num % 10L + '0'); + num /= 10; + if (num <= 0 && endIndex <= maxEnd) + return endIndex; + if (precision == 3) + numberBuffer[--endIndex] = (byte) '.'; + numberBuffer[--endIndex] = (byte) (num % 10L + '0'); + num /= 10; + if (num <= 0 && endIndex <= maxEnd) + return endIndex; + if (precision == 4) + numberBuffer[--endIndex] = (byte) '.'; + numberBuffer[--endIndex] = (byte) (num % 10L + '0'); + num /= 10; + if (num <= 0 && endIndex <= maxEnd) + return endIndex; + if (precision == 5) + numberBuffer[--endIndex] = (byte) '.'; + numberBuffer[--endIndex] = (byte) (num % 10L + '0'); + num /= 10; + if (num <= 0 && endIndex <= maxEnd) + return endIndex; + if (precision == 6) + numberBuffer[--endIndex] = (byte) '.'; + numberBuffer[--endIndex] = (byte) (num % 10L + '0'); + num /= 10; + if (num <= 0 && endIndex <= maxEnd) + return endIndex; + if (precision == 7) + numberBuffer[--endIndex] = (byte) '.'; + numberBuffer[--endIndex] = (byte) (num % 10L + '0'); + num /= 10; + if (num <= 0 && endIndex <= maxEnd) + return endIndex; + if (precision == 8) + numberBuffer[--endIndex] = (byte) '.'; + numberBuffer[--endIndex] = (byte) (num % 10L + '0'); + num /= 10; + if (num <= 0 && endIndex <= maxEnd) + return endIndex; + if (precision == 9) + numberBuffer[--endIndex] = (byte) '.'; + numberBuffer[--endIndex] = (byte) (num % 10L + '0'); + num /= 10; + if (num <= 0 && endIndex <= maxEnd) + return endIndex; + if (precision == 10) + numberBuffer[--endIndex] = (byte) '.'; + numberBuffer[--endIndex] = (byte) (num % 10L + '0'); + num /= 10; + if (num <= 0 && endIndex <= maxEnd) + return endIndex; + if (precision == 11) + numberBuffer[--endIndex] = (byte) '.'; + numberBuffer[--endIndex] = (byte) (num % 10L + '0'); + num /= 10; + if (num <= 0 && endIndex <= maxEnd) + return endIndex; + if (precision == 12) + numberBuffer[--endIndex] = (byte) '.'; + numberBuffer[--endIndex] = (byte) (num % 10L + '0'); + num /= 10; + if (num <= 0 && endIndex <= maxEnd) + return endIndex; + if (precision == 13) + numberBuffer[--endIndex] = (byte) '.'; + numberBuffer[--endIndex] = (byte) (num % 10L + '0'); + num /= 10; + if (num <= 0 && endIndex <= maxEnd) + return endIndex; + if (precision == 14) + numberBuffer[--endIndex] = (byte) '.'; + numberBuffer[--endIndex] = (byte) (num % 10L + '0'); + num /= 10; + if (num <= 0 && endIndex <= maxEnd) + return endIndex; + if (precision == 15) + numberBuffer[--endIndex] = (byte) '.'; + numberBuffer[--endIndex] = (byte) (num % 10L + '0'); + num /= 10; + if (num <= 0 && endIndex <= maxEnd) + return endIndex; + if (precision == 16) + numberBuffer[--endIndex] = (byte) '.'; + numberBuffer[--endIndex] = (byte) (num % 10L + '0'); + num /= 10; + if (num <= 0 && endIndex <= maxEnd) + return endIndex; + if (precision == 17) + numberBuffer[--endIndex] = (byte) '.'; + numberBuffer[--endIndex] = (byte) (num % 10L + '0'); + num /= 10; + if (num <= 0 && endIndex <= maxEnd) + return endIndex; + if (precision == 18) + numberBuffer[--endIndex] = (byte) '.'; + numberBuffer[--endIndex] = (byte) (num % 10L + '0'); + return endIndex; + } + + @NotNull + @Override + public ByteStringAppender append(@NotNull MutableDecimal md) { + StringBuilder sb = acquireUtfReader(); + md.toString(sb); + append(sb); + return this; + } + + @NotNull + @Override + public InputStream inputStream() { + if (inputStream == null) + inputStream = new BytesInputStream(); + return inputStream; + } + + @NotNull + @Override + public OutputStream outputStream() { + if (outputStream == null) + outputStream = new BytesOutputStream(); + return outputStream; + } + + @NotNull + @Override + public BytesMarshallerFactory bytesMarshallerFactory() { + return bytesMarshallerFactory == null ? bytesMarshallerFactory = new VanillaBytesMarshallerFactory() : bytesMarshallerFactory; + } + + @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); + } + + @SuppressWarnings("unchecked") + @Override + public <E> E readEnum(@NotNull Class<E> eClass) { + BytesMarshaller<E> em = bytesMarshallerFactory().acquireMarshaller(eClass, true); + return em.read(this); + } + + @SuppressWarnings("unchecked") + @Override + public <E extends Enum<E>> E parseEnum(@NotNull Class<E> eClass, @NotNull StopCharTester tester) { + String text = parseUTF(tester); + if (text.isEmpty()) + return null; + return Enum.valueOf(eClass, text); + } + + @Override + public <E> void writeList(@NotNull Collection<E> list) { + writeStopBit(list.size()); + for (E e : list) + writeEnum(e); + } + + @Override + public <K, V> void writeMap(@NotNull Map<K, V> map) { + writeStopBit(map.size()); + for (Map.Entry<K, V> entry : map.entrySet()) { + writeEnum(entry.getKey()); + writeEnum(entry.getValue()); + } + } + + @Override + public <E> void readList(@NotNull Collection<E> list, @NotNull Class<E> eClass) { + int len = (int) readStopBit(); + list.clear(); + for (int i = 0; i < len; i++) { + @SuppressWarnings("unchecked") + E e = (E) readEnum(eClass); + list.add(e); + } + } + + @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(); + map.clear(); + for (int i = 0; i < len; i++) + map.put(readEnum(kClass), readEnum(vClass)); + return map; + } + + @Override + public int available() { + return (int) Math.min(Integer.MAX_VALUE, remaining()); + } + + @Override + public int read() { + return remaining() > 0 ? readByte() : -1; + } + + @Override + public int read(@NotNull byte[] bytes) { + return read(bytes, 0, bytes.length); + } + + @Override + public abstract int read(@NotNull byte[] bytes, int off, int len); + + @Override + public long skip(long n) { + if (n < 0) + throw new IllegalArgumentException("Skip bytes out of range, was " + n); + if (n > remaining()) + n = remaining(); + skipBytes((int) n); + return n; + } + + @Override + public void close() { + if (!isFinished()) + finish(); + } + + @Override + public void finish() throws IndexOutOfBoundsException { + if (remaining() < 0) + throwOverflow(); + finished = true; + } + + private void throwOverflow() throws IndexOutOfBoundsException { + throw new IndexOutOfBoundsException("Buffer overflow, capacity: " + capacity() + " position: " + position()); + } + + @Override + public boolean isFinished() { + return finished; + } + + @Override + public void reset() { + finished = false; + position(0L); + } + + @Override + public void flush() { + checkEndOfBuffer(); + } + + @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); + } + } + + @Nullable + @Override + public <T> T readObject(Class<T> tClass) throws IllegalStateException { + Object o = readObject(); + if (o == null || tClass.isInstance(o)) + return (T) o; + throw new ClassCastException("Cannot convert " + o.getClass().getName() + " to " + tClass.getName() + " was " + o); + } + + @SuppressWarnings("unchecked") + @Override + public void writeObject(@Nullable Object obj) { + if (obj == null) { + writeByte(NULL); + return; + } + + 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. + try { + ObjectOutputStream oos = new ObjectOutputStream(this.outputStream()); + oos.writeObject(obj); + } catch (IOException 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 boolean tryLockInt(long offset) { + long id = shortThreadId(); + return tryLockNanos4a(offset, (int) id); + } + + @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)) + return true; + if (nanos <= 10000) + return false; + long end = System.nanoTime() + nanos - 10000; + do { + if (tryLockNanos4a(offset, (int) id)) + return true; + } while (end > System.nanoTime() && !currentThread().isInterrupted()); + return false; + } + + private boolean tryLockNanos4a(long offset, int id) { + int lowId = shortThreadId(); + int firstValue = ((1 << 24) | lowId); + if (compareAndSwapInt(offset, 0, firstValue)) + return true; + long currentValue = readUnsignedInt(offset); + if ((currentValue & INT_LOCK_MASK) == lowId) { + if (currentValue >= (255L << 24)) + throw new IllegalStateException("Reentred 255 times without an unlock"); + currentValue += 1 << 24; + writeOrderedInt(offset, (int) currentValue); + } + return false; + } + + @Override + public void busyLockInt(long offset) throws InterruptedException, IllegalStateException { + boolean success = tryLockNanosInt(offset, BUSY_LOCK_LIMIT); + if (!success) + if (currentThread().isInterrupted()) + throw new InterruptedException(); + else + throw new IllegalStateException("Failed to acquire lock after " + BUSY_LOCK_LIMIT / 1e9 + " seconds."); + } + + @Override + public void unlockInt(long offset) throws IllegalMonitorStateException { + int lowId = shortThreadId(); + int firstValue = ((1 << 24) | lowId); + if (compareAndSwapInt(offset, firstValue, 0)) + return; + // try to chek the lowId and the count. + unlockFailedInt(offset, lowId); + } + + private Thread currentThread; + private int shortThreadId = Integer.MIN_VALUE; + + public int shortThreadId() { + return shortThreadId > 0 ? shortThreadId : shortThreadId0(); + } + + protected int shortThreadId0() { + final int tid = (int) getId() & INT_LOCK_MASK; + if (!ID_LIMIT_WARNED && tid > 1 << 24) { + warnIdLimit(tid); + } + return tid; + } + + public void setCurrentThread() { + currentThread = Thread.currentThread(); + shortThreadId = shortThreadId0(); + } + + public Thread currentThread() { + return currentThread == null ? Thread.currentThread() : currentThread; + } + + @Override + public boolean tryLockLong(long offset) { + long id = uniqueTid(); + return tryLockNanos8a(offset, id); + } + + public long uniqueTid() { + return Jvm.getUniqueTid(currentThread()); + } + + @Override + public boolean tryLockNanosLong(long offset, long nanos) { + long id = uniqueTid(); + int limit = nanos <= 10000 ? (int) nanos / 10 : 1000; + for (int i = 0; i < limit; i++) + if (tryLockNanos8a(offset, id)) + return true; + if (nanos <= 10000) + return false; + long end = System.nanoTime() + nanos - 10000; + do { + if (tryLockNanos8a(offset, id)) + return true; + } while (end > System.nanoTime() && !currentThread().isInterrupted()); + return false; + } + + private boolean tryLockNanos8a(long offset, long id) { + long firstValue = (1L << 48) | id; + if (compareAndSwapLong(offset, 0, firstValue)) + return true; + long currentValue = readLong(offset); + if ((currentValue & (1L << 48) - 1) == id) { + if (currentValue >>> 48 == 65535) + throw new IllegalStateException("Reentred 65535 times without an unlock"); + currentValue += 1L << 48; + writeOrderedLong(offset, currentValue); + return true; + } + return false; + } + + @Override + public void busyLockLong(long offset) throws InterruptedException, IllegalStateException { + boolean success = tryLockNanosLong(offset, BUSY_LOCK_LIMIT); + if (!success) + if (currentThread().isInterrupted()) + throw new InterruptedException(); + else + throw new IllegalStateException("Failed to acquire lock after " + BUSY_LOCK_LIMIT / 1e9 + " seconds."); + } + + @Override + public void unlockLong(long offset) throws IllegalMonitorStateException { + long id = Jvm.getUniqueTid(); + long firstValue = (1L << 48) | id; + if (compareAndSwapLong(offset, firstValue, 0)) + return; + // try to check the lowId and the count. + unlockFailedLong(offset, id); + } + + protected long getId() { + return currentThread().getId(); + } + + private void unlockFailedInt(long offset, int lowId) throws IllegalMonitorStateException { + long currentValue = readUnsignedInt(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"); + } else { + throw new IllegalMonitorStateException("Thread " + holderId + " holds this lock, " + (currentValue >>> 24) + " times"); + } + } + + private void unlockFailedLong(long offset, long id) throws IllegalMonitorStateException { + long currentValue = readLong(offset); + long holderId = currentValue & (-1L >>> 16); + 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)) + + " holds this lock, " + (currentValue >>> 48) + + " times, unlock from " + Jvm.getProcessId() + + " thread " + currentThread().getId()); + } + } + + @Override + public int getAndAdd(long offset, int delta) { + for (; ; ) { + int current = readVolatileInt(offset); + int next = current + delta; + if (compareAndSwapInt(offset, current, next)) + return current; + } + } + + @Override + public int addAndGetInt(long offset, int delta) { + for (; ; ) { + int current = readVolatileInt(offset); + int next = current + delta; + if (compareAndSwapInt(offset, current, next)) + return next; + } + } + + @Override + public byte addByte(long offset, byte b) { + byte b2 = readByte(offset); + b2 += b; + writeByte(offset, b2); + return b2; + } + + @Override + public int addUnsignedByte(long offset, int i) { + int b2 = readUnsignedByte(offset); + b2 += i; + writeUnsignedByte(offset, b2); + return b2 & 0xFF; + } + + @Override + public short addShort(long offset, short s) { + short s2 = readShort(offset); + s2 += s; + writeByte(offset, s2); + return s2; + } + + @Override + public int addUnsignedShort(long offset, int i) { + int b2 = readUnsignedShort(offset); + b2 += i; + writeUnsignedShort(offset, b2); + return b2 & 0xFFFF; + } + + @Override + public int addInt(long offset, int i) { + int b2 = readInt(offset); + b2 += i; + writeInt(offset, b2); + return b2; + } + + @Override + public long addUnsignedInt(long offset, long i) { + long b2 = readUnsignedInt(offset); + b2 += i; + writeUnsignedInt(offset, b2); + return b2 & 0xFFFFFFFFL; + } + + @Override + public long addLong(long offset, long i) { + long b2 = readLong(offset); + b2 += i; + writeLong(offset, b2); + return b2; + } + + @Override + public float addFloat(long offset, float f) { + float b2 = readFloat(offset); + b2 += f; + writeFloat(offset, b2); + return b2; + } + + @Override + public double addDouble(long offset, double d) { + double b2 = readDouble(offset); + b2 += d; + writeDouble(offset, b2); + return b2; + } + + @Override + public int addAtomicInt(long offset, int i) { + return addAndGetInt(offset, i); + } + + @Override + public long addAtomicLong(long offset, long delta) { + for (; ; ) { + long current = readVolatileLong(offset); + long next = current + delta; + if (compareAndSwapLong(offset, current, next)) + return next; + } + } + + @Override + public float addAtomicFloat(long offset, float delta) { + for (; ; ) { + int current = readVolatileInt(offset); + int next = Float.floatToRawIntBits(Float.intBitsToFloat(current) + delta); + if (compareAndSwapInt(offset, current, next)) + return next; + } + } + + @Override + public double addAtomicDouble(long offset, double delta) { + for (; ; ) { + long current = readVolatileLong(offset); + long next = Double.doubleToRawLongBits(Double.longBitsToDouble(current) + delta); + if (compareAndSwapLong(offset, current, next)) + return next; + } + } + + @Override + public float readVolatileFloat(long offset) { + return Float.intBitsToFloat(readVolatileInt(offset)); + } + + @Override + public double readVolatileDouble(long offset) { + return Double.longBitsToDouble(readVolatileLong(offset)); + } + + @Override + public void writeOrderedFloat(long offset, float v) { + writeOrderedInt(offset, Float.floatToRawIntBits(v)); + } + + @Override + public void writeOrderedDouble(long offset, double v) { + writeOrderedLong(offset, Double.doubleToRawLongBits(v)); + } + + @Override + public int length() { + return (int) Math.min(Integer.MAX_VALUE, remaining()); + } + + @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; + } + + @Override + public void readMarshallable(@NotNull Bytes in) throws IllegalStateException { + long len = Math.min(remaining(), in.remaining()); + long inPosition = in.position(); + write(in, inPosition, len); + in.position(inPosition + len); + } + + @Override + public void writeMarshallable(@NotNull Bytes out) { + out.write(this, position(), remaining()); + + } + + @Override + public void write(BytesCommon bytes, long position, long length) { + if (length > bytes.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)); + position += 8; + length -= 8; + } + } + while (length >= 1) { + writeByte(rdi.readByte(position)); + position++; + length--; + } + } + + protected class BytesInputStream extends InputStream { + private long mark = 0; + + @Override + public int available() throws IOException { + long remaining = remaining(); + return (int) Math.min(Integer.MAX_VALUE, remaining); + } + + @Override + public void close() throws IOException { + finish(); + } + + @SuppressWarnings("NonSynchronizedMethodOverridesSynchronizedMethod") + @Override + public void mark(int readlimit) { + mark = position(); + } + + @Override + public boolean markSupported() { + return true; + } + + @Override + public int read(byte[] b, int off, int len) throws IOException { + return AbstractBytes.this.read(b, off, len); + } + + @SuppressWarnings("NonSynchronizedMethodOverridesSynchronizedMethod") + @Override + public void reset() throws IOException { + position(mark); + } + + @Override + public long skip(long n) throws IOException { + if (n > Integer.MAX_VALUE) + throw new IOException("Skip too large"); + return skipBytes((int) n); + } + + @Override + public int read() throws IOException { + if (remaining() > 0) + return readUnsignedByte(); + return -1; + } + } + + protected class BytesOutputStream extends OutputStream { + @Override + public void close() throws IOException { + finish(); + } + + @Override + public void write(@NotNull byte[] b) throws IOException { + AbstractBytes.this.write(b); + } + + @Override + public void write(byte[] b, int off, int len) throws IOException { + AbstractBytes.this.write(b, off, len); + } + + @Override + public void write(int b) throws IOException { + checkWrite(1); + writeUnsignedByte(b); + } + } +} diff --git a/lang/src/main/java/net/openhft/lang/io/ByteBufferBytes.java b/lang/src/main/java/net/openhft/lang/io/ByteBufferBytes.java new file mode 100755 index 0000000..5b85e87 --- /dev/null +++ b/lang/src/main/java/net/openhft/lang/io/ByteBufferBytes.java @@ -0,0 +1,441 @@ +/* + * 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.io; + +import org.jetbrains.annotations.NotNull; +import sun.nio.ch.DirectBuffer; + +import java.io.EOFException; +import java.nio.ByteBuffer; +import java.nio.ByteOrder; +import java.util.concurrent.atomic.AtomicBoolean; + +/** + * @author peter.lawrey + */ +public class ByteBufferBytes extends AbstractBytes { + protected final ByteBuffer buffer; + protected int start, position, limit; + protected AtomicBoolean barrier; + + public ByteBufferBytes(ByteBuffer buffer) { + this(buffer, 0, buffer.capacity()); + } + + public ByteBufferBytes(ByteBuffer buffer, int start, int limit) { + this.buffer = buffer; + this.start = position = start; + this.limit = limit; + } + + public ByteBuffer buffer() { + return buffer; + } + + protected void readBarrier() { + if (barrier == null) barrier = new AtomicBoolean(); + barrier.get(); + } + + protected 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(); + long left = remaining(); + if (left <= 0) return -1; + int len2 = (int) Math.min(left, len); + for (int i = 0; i < len2; i++) + bytes[off + i] = readByte(); + return len2; + } + + @Override + public byte readByte() { + if (position < limit) + return buffer.get(position++); + throw new IndexOutOfBoundsException(); + } + + @Override + public byte readByte(long offset) { + int pos = (int) (start + offset); + if (pos < limit) + 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(); + long left = remaining(); + if (left < len) + throw new IllegalStateException(new EOFException()); + for (int i = 0; i < len; i++) + b[off + i] = readByte(); + } + + @Override + public short readShort() { + if (position + 2 <= limit) { + short s = buffer.getShort(position); + position += 2; + return s; + } + throw new IndexOutOfBoundsException(); + } + + @Override + public short readShort(long offset) { + int pos = (int) (start + offset); + if (pos + 2 <= limit) + return buffer.getShort(pos); + throw new IndexOutOfBoundsException(); + } + + @Override + public char readChar() { + if (position + 2 <= limit) { + char ch = buffer.getChar(position); + position += 2; + return ch; + } + throw new IndexOutOfBoundsException(); + } + + @Override + public char readChar(long offset) { + int pos = (int) (start + offset); + if (pos + 2 <= limit) + return buffer.getChar(pos); + throw new IndexOutOfBoundsException(); + } + + @Override + public int readInt() { + if (position + 4 <= limit) { + int i = buffer.getInt(position); + position += 4; + return i; + } + throw new IndexOutOfBoundsException(); + } + + @Override + public int readInt(long offset) { + int pos = (int) (start + offset); + if (pos + 4 <= limit) + return buffer.getInt(pos); + throw new IndexOutOfBoundsException(); + } + + @Override + public int readVolatileInt() { + readBarrier(); + return readInt(); + } + + @Override + public int readVolatileInt(long offset) { + readBarrier(); + return readInt(offset); + } + + @Override + public long readLong() { + if (position + 8 <= limit) { + long l = buffer.getLong(position); + position += 8; + return l; + } + throw new IndexOutOfBoundsException(); + } + + @Override + public long readLong(long offset) { + int pos = (int) (start + offset); + if (pos + 8 <= limit) + return buffer.getLong(pos); + throw new IndexOutOfBoundsException(); + } + + @Override + public long readVolatileLong() { + readBarrier(); + return readLong(); + } + + @Override + public long readVolatileLong(long offset) { + readBarrier(); + return readLong(offset); + } + + @Override + public float readFloat() { + if (position + 4 <= limit) { + float f = buffer.getFloat(position); + position += 4; + return f; + } + throw new IndexOutOfBoundsException(); + } + + @Override + public float readFloat(long offset) { + int pos = (int) (start + offset); + if (pos + 4 <= limit) + return buffer.getFloat(pos); + throw new IndexOutOfBoundsException(); + } + + @Override + public double readDouble() { + if (position + 8 <= limit) { + double d = buffer.getDouble(position); + position += 8; + return d; + } + throw new IndexOutOfBoundsException(); + } + + @Override + public double readDouble(long offset) { + int pos = (int) (start + offset); + if (pos + 8 <= limit) + return buffer.getDouble(pos); + throw new IndexOutOfBoundsException(); + } + + @Override + public void write(int b) { + if (position < limit) + buffer.put(position++, (byte) b); + else + throw new IndexOutOfBoundsException(); + } + + @Override + public void writeByte(long offset, int b) { + int pos = (int) (start + offset); + if (pos < limit) + buffer.put(pos, (byte) b); + else + throw new IndexOutOfBoundsException(); + } + + @Override + public void writeShort(int v) { + if (position + 2 <= limit) { + buffer.putShort(position, (short) v); + position += 2; + } else { + throw new IndexOutOfBoundsException(); + } + } + + @Override + public void writeShort(long offset, int v) { + int pos = (int) (start + offset); + if (pos + 2 <= limit) + buffer.putShort(pos, (short) v); + else + throw new IndexOutOfBoundsException(); + } + + @Override + public void writeChar(int v) { + if (position + 2 <= limit) { + buffer.putChar(position, (char) v); + position += 2; + } else { + throw new IndexOutOfBoundsException(); + } + } + + @Override + public void writeChar(long offset, int v) { + int pos = (int) (start + offset); + if (pos + 2 <= limit) + buffer.putChar(pos, (char) v); + else + throw new IndexOutOfBoundsException(); + } + + @Override + public void writeInt(int v) { + if (position + 4 <= limit) { + buffer.putInt(position, v); + position += 4; + } else { + throw new IndexOutOfBoundsException(); + } + } + + @Override + public void writeInt(long offset, int v) { + int pos = (int) (start + offset); + if (pos + 4 <= limit) + buffer.putInt(pos, v); + else + throw new IndexOutOfBoundsException(); + } + + @Override + public void writeOrderedInt(int v) { + writeInt(v); + writeBarrier(); + } + + @Override + public void writeOrderedInt(long offset, int v) { + writeInt(offset, v); + writeBarrier(); + } + + @Override + public boolean compareAndSwapInt(long offset, int expected, int x) { + if (buffer instanceof DirectBuffer) + return NativeBytes.UNSAFE.compareAndSwapInt(null, ((DirectBuffer) buffer).address() + offset, expected, x); + return NativeBytes.UNSAFE.compareAndSwapInt(buffer.array(), NativeBytes.BYTES_OFFSET + offset, expected, x); + } + + @Override + public void writeLong(long v) { + if (position + 8 <= limit) { + buffer.putLong(position, v); + position += 8; + } else { + throw new IndexOutOfBoundsException(); + } + } + + @Override + public void writeLong(long offset, long v) { + int pos = (int) (start + offset); + if (pos + 8 <= limit) + buffer.putLong(pos, v); + else + throw new IndexOutOfBoundsException(); + } + + @Override + public void writeOrderedLong(long v) { + writeLong(v); + writeBarrier(); + } + + @Override + public void writeOrderedLong(long offset, long v) { + writeLong((int) offset, v); + writeBarrier(); + } + + @Override + public boolean compareAndSwapLong(long offset, long expected, long x) { + if (buffer instanceof DirectBuffer) + return NativeBytes.UNSAFE.compareAndSwapLong(null, ((DirectBuffer) buffer).address() + offset, expected, x); + return NativeBytes.UNSAFE.compareAndSwapLong(buffer.array(), NativeBytes.BYTES_OFFSET + offset, expected, x); + } + + @Override + public void writeFloat(float v) { + if (position + 4 <= limit) { + buffer.putFloat(position, v); + position += 4; + } else { + throw new IndexOutOfBoundsException(); + } + } + + @Override + public void writeFloat(long offset, float v) { + int pos = (int) (start + offset); + if (pos + 4 <= limit) + buffer.putFloat(pos, v); + else + throw new IndexOutOfBoundsException(); + } + + @Override + public void writeDouble(double v) { + if (position + 8 <= limit) { + buffer.putDouble(position, v); + position += 8; + } else { + throw new IndexOutOfBoundsException(); + } + } + + @Override + public void writeDouble(long offset, double v) { + int pos = (int) (start + offset); + if (pos + 8 <= limit) + buffer.putDouble(pos, v); + else + throw new IndexOutOfBoundsException(); + } + + @Override + public void readObject(Object object, int start, int end) { + throw new UnsupportedOperationException(); + } + + @Override + public void writeObject(Object object, int start, int end) { + throw new UnsupportedOperationException(); + } + + @Override + public long position() { + return position - start; + } + + @Override + public void position(long position) { + if (start + position > Integer.MAX_VALUE) + throw new IndexOutOfBoundsException("Position to large"); + this.position = (int) (start + position); + } + + @Override + public long capacity() { + return limit - start; + } + + @Override + public long remaining() { + return limit - position; + } + + @NotNull + @Override + public ByteOrder byteOrder() { + return buffer.order(); + } + + @Override + public void checkEndOfBuffer() throws IndexOutOfBoundsException { + if (position < start || position > limit) + throw new IndexOutOfBoundsException(); + } +} diff --git a/lang/src/main/java/net/openhft/lang/io/ByteStringAppender.java b/lang/src/main/java/net/openhft/lang/io/ByteStringAppender.java new file mode 100755 index 0000000..fd36102 --- /dev/null +++ b/lang/src/main/java/net/openhft/lang/io/ByteStringAppender.java @@ -0,0 +1,80 @@ +/* + * 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.io; + +import org.jetbrains.annotations.NotNull; +import org.jetbrains.annotations.Nullable; + +/** + * @author peter.lawrey + */ +public interface ByteStringAppender extends Appendable, BytesCommon { + @NotNull + ByteStringAppender append(@NotNull CharSequence s); + + @NotNull + ByteStringAppender append(@NotNull CharSequence s, int start, int end); + + /** + * Writes "true" or "false". + * + * @param b to write. + * @return this. + */ + @NotNull + ByteStringAppender append(boolean b); + + @NotNull + ByteStringAppender append(char c); + + @NotNull + ByteStringAppender append(@Nullable Enum value); + + @NotNull + ByteStringAppender append(int i); + + @NotNull + ByteStringAppender append(long l); + + @NotNull + ByteStringAppender append(long l, int base); + + @NotNull + ByteStringAppender appendTimeMillis(long timeInMS); + + @NotNull + ByteStringAppender appendDateMillis(long timeInMS); + + @NotNull + ByteStringAppender appendDateTimeMillis(long timeInMS); + +// ByteStringAppender append(float f); + +// ByteStringAppender append(float f, int precision); + + @NotNull + ByteStringAppender append(double d); + + @NotNull + ByteStringAppender append(double d, int precision); + + @NotNull + ByteStringAppender append(@NotNull MutableDecimal md); + + @NotNull + <E> ByteStringAppender append(@NotNull Iterable<E> list, @NotNull CharSequence separator); +} diff --git a/lang/src/main/java/net/openhft/lang/io/ByteStringParser.java b/lang/src/main/java/net/openhft/lang/io/ByteStringParser.java new file mode 100755 index 0000000..10132cc --- /dev/null +++ b/lang/src/main/java/net/openhft/lang/io/ByteStringParser.java @@ -0,0 +1,83 @@ +/* + * 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.io; + +import org.jetbrains.annotations.NotNull; +import org.jetbrains.annotations.Nullable; + +/** + * @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 + * + * @param tester to detect the end of the text. + * @return true, false, or null if neither. + */ + Boolean parseBoolean(@NotNull StopCharTester tester); + + /** + * Populate a StringBuilder with the UTF encoded text until the end. + * + * @param builder to clear and append to. + * @param tester to detect when to stop. + */ + void parseUTF(@NotNull StringBuilder builder, @NotNull StopCharTester tester); + + @NotNull + String parseUTF(@NotNull StopCharTester tester); + + @Nullable + <E extends Enum<E>> E parseEnum(@NotNull Class<E> eClass, @NotNull StopCharTester tester); + + @NotNull + MutableDecimal parseDecimal(@NotNull MutableDecimal decimal); + + /** + * @return the next long, stopping at the first invalid character + */ + long parseLong(); + + /** + * @param base to use. + * @return the next long, stopping at the first invalid character + */ + long parseLong(int base); + + double parseDouble(); + + /** + * Make sure we just read a stop character + * + * @param tester to stop at + * @return true if we stopped at a stop character, false if we ran out of data. + */ + boolean stepBackAndSkipTo(@NotNull StopCharTester tester); + + /** + * Wind from this positionAddr to the end of the field + * + * @param tester to stop at + * @return true if we stopped at a stop character, false if we ran out of data. + */ + boolean skipTo(@NotNull StopCharTester tester); +} diff --git a/lang/src/main/java/net/openhft/lang/io/Bytes.java b/lang/src/main/java/net/openhft/lang/io/Bytes.java new file mode 100755 index 0000000..c520613 --- /dev/null +++ b/lang/src/main/java/net/openhft/lang/io/Bytes.java @@ -0,0 +1,26 @@ +/* + * 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.io; + +import net.openhft.lang.io.serialization.BytesMarshallable; + +/** + * @author peter.lawrey + */ +public interface Bytes extends RandomDataInput, RandomDataOutput, RandomDataUpdate, + ByteStringAppender, ByteStringParser, CharSequence, BytesMarshallable { +} diff --git a/lang/src/main/java/net/openhft/lang/io/BytesCommon.java b/lang/src/main/java/net/openhft/lang/io/BytesCommon.java new file mode 100755 index 0000000..0549f61 --- /dev/null +++ b/lang/src/main/java/net/openhft/lang/io/BytesCommon.java @@ -0,0 +1,104 @@ +/* + * 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.io; + +import net.openhft.lang.io.serialization.BytesMarshallerFactory; +import org.jetbrains.annotations.NotNull; + +import java.io.InputStream; +import java.io.OutputStream; +import java.nio.ByteOrder; + +/** + * @author peter.lawrey + */ +public interface BytesCommon { + /** + * @return the offset read/written so far + */ + long position(); + + /** + * @param position to skip to + */ + void position(long position); + + /** + * @return space available + */ + long capacity(); + + /** + * @return space remaining in bytes + */ + long remaining(); + + /** + * Mark the end of the message if writing and check we are at the end of the message if reading. + * + * @throws IndexOutOfBoundsException if too much data was written. + */ + void finish() throws IndexOutOfBoundsException; + + /** + * @return has finish been called. + */ + boolean isFinished(); + + /** + * Start again, unfinished, position() == 0 + */ + void reset(); + + /** + * @return Byte order for reading binary + */ + @NotNull + ByteOrder byteOrder(); + + /** + * @return these Bytes as an InputStream + */ + @NotNull + InputStream inputStream(); + + /** + * @return these Bytes as an OutputStream + */ + @NotNull + OutputStream outputStream(); + + /** + * @return the factory for marshallers. + */ + @NotNull + BytesMarshallerFactory bytesMarshallerFactory(); + + /** + * @throws IndexOutOfBoundsException if the bounds of the Bytes has been exceeded. + */ + void checkEndOfBuffer() throws IndexOutOfBoundsException; + + /** + * Copy from one Bytes to another, moves the position by length + * + * @param bytes to copy + * @param position to copy from + * @param length to copy + */ + void write(BytesCommon bytes, long position, long length); +} diff --git a/lang/src/main/java/net/openhft/lang/io/DirectBytes.java b/lang/src/main/java/net/openhft/lang/io/DirectBytes.java new file mode 100755 index 0000000..7e783e4 --- /dev/null +++ b/lang/src/main/java/net/openhft/lang/io/DirectBytes.java @@ -0,0 +1,43 @@ +/* + * 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.io; + +import org.jetbrains.annotations.NotNull; + +/** + * @author peter.lawrey + */ +public class DirectBytes extends NativeBytes { + @NotNull + private final DirectStore store; + + DirectBytes(@NotNull DirectStore store) { + super(store.bytesMarshallerFactory, store.address, store.address, store.address + store.size); + this.store = store; + } + + public void positionAndSize(long offset, long size) { + if (offset < 0 || size < 0 || offset + size > store.size) + throw new IllegalArgumentException(); + startAddr = positionAddr = store.address + offset; + limitAddr = startAddr + size; + } + + public DirectStore store() { + return 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 new file mode 100755 index 0000000..7099165 --- /dev/null +++ b/lang/src/main/java/net/openhft/lang/io/DirectStore.java @@ -0,0 +1,96 @@ +/* + * 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.io; + +import net.openhft.lang.io.serialization.BytesMarshallerFactory; +import net.openhft.lang.io.serialization.impl.VanillaBytesMarshallerFactory; +import org.jetbrains.annotations.NotNull; +import sun.misc.Cleaner; + +/** + * @author peter.lawrey + */ +public class DirectStore { + protected BytesMarshallerFactory bytesMarshallerFactory; + private final Cleaner cleaner; + protected long address; + protected long size; + + public DirectStore(long size) { + this(new VanillaBytesMarshallerFactory(), size); + } + + public DirectStore(BytesMarshallerFactory bytesMarshallerFactory, long size) { + this(bytesMarshallerFactory, size, true); + } + + public DirectStore(BytesMarshallerFactory bytesMarshallerFactory, long size, boolean zeroOut) { + this.bytesMarshallerFactory = bytesMarshallerFactory; + address = NativeBytes.UNSAFE.allocateMemory(size); + +// System.out.println("old value " + Integer.toHexString(NativeBytes.UNSAFE.getInt(null, address))); + if (zeroOut) { + NativeBytes.UNSAFE.setMemory(address, size, (byte) 0); + NativeBytes.UNSAFE.putLongVolatile(null, address, 0L); + } + + 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; + } + }); + } + + @NotNull + public static DirectStore allocate(long size) { + return new DirectStore(null, size); + } + + @NotNull + public static DirectStore allocateLazy(long size) { + return new DirectStore(null, size, false); + } + +/* public void resize(long newSize) { + if (newSize == size) + return; + address = NativeBytes.UNSAFE.reallocateMemory(address, newSize); + size = newSize; + }*/ + + @NotNull + public DirectBytes createSlice() { + return new DirectBytes(this); + } + + public void free() { + cleaner.clean(); + } + + public long size() { + return size; + } + + public BytesMarshallerFactory bytesMarshallerFactory() { + return bytesMarshallerFactory; + } + +} diff --git a/lang/src/main/java/net/openhft/lang/io/IOTools.java b/lang/src/main/java/net/openhft/lang/io/IOTools.java new file mode 100755 index 0000000..854e8fe --- /dev/null +++ b/lang/src/main/java/net/openhft/lang/io/IOTools.java @@ -0,0 +1,79 @@ +/* + * 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.io; + +import org.jetbrains.annotations.NotNull; +import org.jetbrains.annotations.Nullable; + +import java.io.Closeable; +import java.io.File; +import java.io.IOException; +import java.nio.charset.Charset; +import java.util.logging.Logger; + +/** + * @author peter.lawrey + */ +public enum IOTools { + ; + public static final Charset ISO_8859_1 = Charset.forName("ISO-8859-1"); + public static final Charset UTF_8 = Charset.forName("UTF-8"); + + public static void close(@Nullable Closeable closeable) { + if (closeable == null) return; + try { + closeable.close(); + } catch (IOException ignored) { + } + } + + public static void close(@NotNull Iterable<? extends Closeable> closeables) { + for (Closeable closeable : closeables) { + close(closeable); + } + } + + public static int stopBitLength(long l) { + if (l < 0) return stopBitLength(~l) + 1; + int count = 1; + while (l >= 128) { + l >>>= 7; + count++; + } + return count; + } + + public static void deleteDir(String dirPath) { + deleteDir(new File(dirPath)); + } + + public static void deleteDir(File dir) { + // delete one level. + if (dir.isDirectory()) { + File[] files = dir.listFiles(); + if (files != null) + for (File file : files) + if (file.isDirectory()) { + deleteDir(file); + } else if (!file.delete()) { + Logger.getLogger(IOTools.class.getName()).info("... unable to delete " + file); + } + + } + dir.delete(); + } +} diff --git a/lang/src/main/java/net/openhft/lang/io/MappedFile.java b/lang/src/main/java/net/openhft/lang/io/MappedFile.java new file mode 100644 index 0000000..f4907b9 --- /dev/null +++ b/lang/src/main/java/net/openhft/lang/io/MappedFile.java @@ -0,0 +1,146 @@ +/* + * 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.io; + +import org.jetbrains.annotations.NotNull; + +import java.io.FileNotFoundException; +import java.io.IOException; +import java.io.RandomAccessFile; +import java.nio.ByteOrder; +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 long blockSize; + private final long overlapSize; + 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 basePath, long blockSize, long overlapSize) throws FileNotFoundException { + this.basePath = basePath; + this.blockSize = blockSize; + this.overlapSize = overlapSize; + fileChannel = new RandomAccessFile(basePath, "rw").getChannel(); + } + + public static MappedByteBuffer getMap(@NotNull FileChannel fileChannel, long start, int size) throws IOException { + for (int i = 1; ; i++) { + try { +// long startTime = System.nanoTime(); + @SuppressWarnings("UnnecessaryLocalVariable") + MappedByteBuffer map = fileChannel.map(FileChannel.MapMode.READ_WRITE, start, size); + map.order(ByteOrder.nativeOrder()); +// long time = System.nanoTime() - startTime; +// System.out.printf("Took %,d us to map %,d MB%n", time / 1000, size / 1024 / 1024); +// System.out.println("Map size: "+size); + return map; + } catch (IOException e) { + 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; + } + } + } + } + + public MappedMemory acquire(long index) throws IOException { + return acquire(index, false); + } + + public MappedMemory acquire(long index, boolean prefetch) throws IOException { + MappedMemory map0 = this.map0, map1 = this.map1; + if (map0 != null && map0.index() == index) { + map0.reserve(); + return map0; + } + if (map1 != null && map1.index() == index) { + map1.reserve(); + return map1; + } + return acquire0(index, prefetch); + } + + private synchronized MappedMemory acquire0(long index, boolean prefetch) throws IOException { + if (map1 != null) + map1.release(); + map1 = map0; + map0 = new MappedMemory(fileChannel.map(FileChannel.MapMode.READ_WRITE, index * blockSize, blockSize + overlapSize), index); + map0.reserve(); + maps.add(map0); + // clean up duds. + for (int i = maps.size() - 1; i >= 0; i--) { + if (maps.get(i).refCount() <= 0) + maps.remove(i); + } + return map0; + } + + public synchronized void close() throws IOException { + if (map1 != null) { + map1.release(); + map1 = null; + } + if (map0 != null) { + map0.release(); + map0 = null; + } + // clean up errant maps. + int count = 0; + for (int i = maps.size() - 1; i >= 0; i--) { + if (maps.get(i).refCount() <= 0) { + maps.get(i).close(); + count++; + } + } + if (count > 1) + Logger.getLogger(MappedFile.class.getName()).info(basePath + " memory mappings left unreleased, num= " + count); + maps.clear(); + fileChannel.close(); + } + + public long size() { + try { + return fileChannel.size(); + } catch (IOException e) { + return 0; + } + } +} diff --git a/lang/src/main/java/net/openhft/lang/io/MappedMemory.java b/lang/src/main/java/net/openhft/lang/io/MappedMemory.java new file mode 100644 index 0000000..df97ef5 --- /dev/null +++ b/lang/src/main/java/net/openhft/lang/io/MappedMemory.java @@ -0,0 +1,83 @@ +/* + * 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.io; + +import sun.misc.Cleaner; +import sun.nio.ch.DirectBuffer; + +import java.nio.MappedByteBuffer; +import java.util.concurrent.atomic.AtomicInteger; + +public class MappedMemory { + private final MappedByteBuffer buffer; + private final long index; + private final AtomicInteger refCount = new AtomicInteger(1); + 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(); + } + + public long index() { + return index; + } + + public void reserve() { + if (unmapped) throw new IllegalStateException(); + refCount.incrementAndGet(); + } + + public void release() { + if (unmapped) throw new IllegalStateException(); + if (refCount.decrementAndGet() > 0) return; + close(); + } + + public MappedByteBuffer buffer() { + return buffer; + } + + public long address() { + return ((DirectBuffer) buffer).address(); + } + + public int refCount() { + return refCount.get(); + } + + public static void release(MappedMemory mapmem) { + if (mapmem != null) + mapmem.release(); + } + + public void force() { + if (!unmapped) + buffer.force(); + } + + public void close() { + unmap(buffer); + unmapped = true; + } +} diff --git a/lang/src/main/java/net/openhft/lang/io/MultiStoreBytes.java b/lang/src/main/java/net/openhft/lang/io/MultiStoreBytes.java new file mode 100644 index 0000000..c2b6679 --- /dev/null +++ b/lang/src/main/java/net/openhft/lang/io/MultiStoreBytes.java @@ -0,0 +1,40 @@ +/* + * 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.io; + +public class MultiStoreBytes extends NativeBytes { + private DirectStore store; + + public MultiStoreBytes() { + super(NO_PAGE, NO_PAGE, NO_PAGE); + this.store = store; + } + + 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 DirectStore store() { + return store; + } +} + diff --git a/lang/src/main/java/net/openhft/lang/io/MutableDecimal.java b/lang/src/main/java/net/openhft/lang/io/MutableDecimal.java new file mode 100755 index 0000000..773ce07 --- /dev/null +++ b/lang/src/main/java/net/openhft/lang/io/MutableDecimal.java @@ -0,0 +1,205 @@ +/* + * 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.io; + +import org.jetbrains.annotations.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]; + + static { + TENS[0] = 1; + for (int i = 1; i < TENS.length; i++) + TENS[i] = 10 * TENS[i - 1]; + } + + private long value; + private int scale; + + public MutableDecimal() { + this(0, Integer.MIN_VALUE); + } + + public MutableDecimal(long value, int scale) { + this.value = value; + this.scale = scale; + } + + public MutableDecimal(long value) { + this(value, 0); + } + + public MutableDecimal(double d, int precision) { + 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(); + } + } + + public void set(long value, int scale) { + this.value = value; + this.scale = scale; + } + + public long value() { + return value; + } + + public int scale() { + return scale; + } + + public void clear() { + scale = Integer.MIN_VALUE; + } + + public boolean isSet() { + return scale > Integer.MIN_VALUE; + } + + @Override + public int hashCode() { + int hash = (int) value; + hash ^= value >>> 32; + hash ^= scale * 37; + return hash; + } + + @Override + public boolean equals(Object obj) { + if (!(obj instanceof MutableDecimal)) + return false; + MutableDecimal md = (MutableDecimal) obj; + return value == md.value && scale == md.scale; + } + + @NotNull + @Override + public String toString() { + StringBuilder sb = new StringBuilder(20); + toString(sb); + return sb.toString(); + } + + public void toString(@NotNull StringBuilder sb) { + if (scale == Integer.MIN_VALUE) { + sb.append("not set"); + return; + } + if (value == 0 && scale <= 0) { + sb.append('0'); + return; + } + + boolean neg = false; + long v = value; + int s = scale; + if (v < 0) { + v = -value; + neg = true; + } + for (int s2 = scale; s2 < 0; s2++) + sb.append('0'); + while (v != 0 || s >= 0) { + int digit = (int) (v % 10); + // MIN_VALUE + if (digit < 0) { + digit = 8; + v = (v >>> 1) / 5; + } else { + v /= 10; + } + sb.append((char) ('0' + digit)); + if (--s == 0) + sb.append('.'); + } + if (neg) + sb.append('-'); + sb.reverse(); + } + + @NotNull + @Override + public MutableDecimal clone() throws CloneNotSupportedException { + return (MutableDecimal) super.clone(); + } + + @Override + public int intValue() { + return (int) longValue(); + } + + @Override + public long longValue() { + if (scale == 0) + return value; + if (scale > 0 && scale < TENS.length) + return value / (long) TENS[scale]; + if (scale < 0 && -scale < TENS.length) + return value * (long) TENS[-scale]; + return (long) doubleValue(); + } + + @Override + public float floatValue() { + return (float) doubleValue(); + } + + public double doubleValue() { + if (scale == 0) + return value; + return scale <= 0 ? value * tens(-scale) : value / tens(scale); + } + + private static double tens(int scale) { + return scale < TENS.length ? TENS[scale] : Math.pow(10, scale); + } + + @Override + public int compareTo(@NotNull MutableDecimal o) { + long value = this.value, ovalue = o.value; + if (scale == o.scale) + return longCompareTo(value, ovalue); + if (value == 0 && ovalue == 0) + return 0; + double d = doubleValue(), od = o.doubleValue(); + double err = (Math.abs(d) + Math.abs(od)) / 1e15; + if (d + err < od) return -1; + if (d > od + err) return +1; + // fallback. + return BigDecimal.valueOf(value, scale).compareTo(BigDecimal.valueOf(ovalue, o.scale())); + } + + private static int longCompareTo(long value, long ovalue) { + return value < ovalue ? -1 : value > ovalue ? +1 : 0; + } +} diff --git a/lang/src/main/java/net/openhft/lang/io/NativeBytes.java b/lang/src/main/java/net/openhft/lang/io/NativeBytes.java new file mode 100755 index 0000000..2a974d7 --- /dev/null +++ b/lang/src/main/java/net/openhft/lang/io/NativeBytes.java @@ -0,0 +1,416 @@ +/* + * 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.io; + +import net.openhft.lang.io.serialization.BytesMarshallerFactory; +import org.jetbrains.annotations.NotNull; +import sun.misc.Unsafe; + +import java.io.EOFException; +import java.lang.reflect.Field; +import java.nio.ByteOrder; + +/** + * @author peter.lawrey + */ +public class NativeBytes extends AbstractBytes { + /** + * *** Access the Unsafe class ***** + */ + @NotNull + @SuppressWarnings("ALL") + public static final Unsafe UNSAFE; + static final int BYTES_OFFSET; + + static { + try { + @SuppressWarnings("ALL") + Field theUnsafe = Unsafe.class.getDeclaredField("theUnsafe"); + theUnsafe.setAccessible(true); + UNSAFE = (Unsafe) theUnsafe.get(null); + BYTES_OFFSET = UNSAFE.arrayBaseOffset(byte[].class); + + } catch (Exception e) { + throw new AssertionError(e); + } + } + + protected static final long NO_PAGE = UNSAFE.allocateMemory(UNSAFE.pageSize()); + + protected long startAddr; + protected long positionAddr; + protected long limitAddr; + + public NativeBytes(long startAddr, long positionAddr, long limitAddr) { + this.startAddr = startAddr; + this.positionAddr = positionAddr; + this.limitAddr = limitAddr; + } + + public NativeBytes(BytesMarshallerFactory bytesMarshallerFactory, long startAddr, long positionAddr, long limitAddr) { + super(bytesMarshallerFactory); + this.startAddr = startAddr; + this.positionAddr = positionAddr; + this.limitAddr = limitAddr; + } + + public NativeBytes(NativeBytes bytes) { + super(bytes.bytesMarshallerFactory()); + this.startAddr = bytes.startAddr; + this.positionAddr = bytes.positionAddr; + this.limitAddr = bytes.limitAddr; + } + + public static long longHash(byte[] bytes, int off, int len) { + long hash = 0; + int pos = 0; + for (; pos < len - 7; pos += 8) + hash = hash * 10191 + UNSAFE.getLong(bytes, (long) BYTES_OFFSET + off + pos); + for (; pos < len; pos++) + hash = hash * 57 + bytes[off + pos]; + return hash; + } + + @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); + UNSAFE.copyMemory(null, positionAddr, bytes, BYTES_OFFSET + off, len2); + positionAddr += len2; + return len2; + } + + @Override + public byte readByte() { + return UNSAFE.getByte(positionAddr++); + } + + @Override + public byte readByte(long offset) { + return UNSAFE.getByte(startAddr + offset); + } + + @Override + public void readFully(@NotNull byte[] b, int off, int len) { + if (len < 0 || off < 0 || off + len > b.length) + throw new IllegalArgumentException(); + long left = remaining(); + if (left < len) + throw new IllegalStateException(new EOFException()); + UNSAFE.copyMemory(null, positionAddr, b, BYTES_OFFSET + off, len); + positionAddr += len; + } + + @Override + public short readShort() { + short s = UNSAFE.getShort(positionAddr); + positionAddr += 2; + return s; + } + + @Override + public short readShort(long offset) { + return UNSAFE.getShort(startAddr + offset); + } + + @Override + public char readChar() { + char ch = UNSAFE.getChar(positionAddr); + positionAddr += 2; + return ch; + } + + @Override + public char readChar(long offset) { + return UNSAFE.getChar(startAddr + offset); + } + + @Override + public int readInt() { + int i = UNSAFE.getInt(positionAddr); + positionAddr += 4; + return i; + } + + @Override + public int readInt(long offset) { + return UNSAFE.getInt(startAddr + offset); + } + + @Override + public int readVolatileInt() { + int i = UNSAFE.getIntVolatile(null, positionAddr); + positionAddr += 4; + return i; + } + + @Override + public int readVolatileInt(long offset) { + return UNSAFE.getIntVolatile(null, startAddr + offset); + } + + @Override + public long readLong() { + long l = UNSAFE.getLong(positionAddr); + positionAddr += 8; + return l; + } + + @Override + public long readLong(long offset) { + return UNSAFE.getLong(startAddr + offset); + } + + @Override + public long readVolatileLong() { + long l = UNSAFE.getLongVolatile(null, positionAddr); + positionAddr += 8; + return l; + } + + @Override + public long readVolatileLong(long offset) { + return UNSAFE.getLongVolatile(null, startAddr + offset); + } + + @Override + public float readFloat() { + float f = UNSAFE.getFloat(positionAddr); + positionAddr += 4; + return f; + } + + @Override + public float readFloat(long offset) { + return UNSAFE.getFloat(startAddr + offset); + } + + @Override + public double readDouble() { + double d = UNSAFE.getDouble(positionAddr); + positionAddr += 8; + return d; + } + + @Override + public double readDouble(long offset) { + return UNSAFE.getDouble(startAddr + offset); + } + + @Override + public void write(int b) { + UNSAFE.putByte(positionAddr++, (byte) b); + } + + @Override + public void writeByte(long offset, int b) { + UNSAFE.putByte(startAddr + offset, (byte) b); + } + + @Override + public void write(long offset, @NotNull byte[] bytes) { + if (offset < 0 || offset + bytes.length > capacity()) + throw new IllegalArgumentException(); + UNSAFE.copyMemory(bytes, BYTES_OFFSET, null, startAddr + offset, bytes.length); + positionAddr += bytes.length; + } + + @Override + public void write(byte[] bytes, int off, int len) { + UNSAFE.copyMemory(bytes, BYTES_OFFSET + off, null, positionAddr, len); + positionAddr += len; + } + + @Override + public void writeShort(int v) { + UNSAFE.putShort(positionAddr, (short) v); + positionAddr += 2; + } + + @Override + public void writeShort(long offset, int v) { + UNSAFE.putShort(startAddr + offset, (short) v); + } + + @Override + public void writeChar(int v) { + UNSAFE.putChar(positionAddr, (char) v); + positionAddr += 2; + } + + @Override + public void writeChar(long offset, int v) { + UNSAFE.putChar(startAddr + offset, (char) v); + } + + @Override + public void writeInt(int v) { + UNSAFE.putInt(null, positionAddr, v); + positionAddr += 4; + } + + @Override + public void writeInt(long offset, int v) { + UNSAFE.putInt(startAddr + offset, v); + } + + @Override + public void writeOrderedInt(int v) { + UNSAFE.putOrderedInt(null, positionAddr, v); + positionAddr += 4; + } + + @Override + public void writeOrderedInt(long offset, int v) { + UNSAFE.putOrderedInt(null, startAddr + offset, v); + } + + @Override + public boolean compareAndSwapInt(long offset, int expected, int x) { + return UNSAFE.compareAndSwapInt(null, startAddr + offset, expected, x); + } + + @Override + public void writeLong(long v) { + UNSAFE.putLong(positionAddr, v); + positionAddr += 8; + } + + @Override + public void writeLong(long offset, long v) { + UNSAFE.putLong(startAddr + offset, v); + } + + @Override + public void writeOrderedLong(long v) { + UNSAFE.putOrderedLong(null, positionAddr, v); + positionAddr += 8; + } + + @Override + public void writeOrderedLong(long offset, long v) { + UNSAFE.putOrderedLong(null, startAddr + offset, v); + } + + @Override + public boolean compareAndSwapLong(long offset, long expected, long x) { + return UNSAFE.compareAndSwapLong(null, startAddr + offset, expected, x); + } + + @Override + public void writeFloat(float v) { + UNSAFE.putFloat(positionAddr, v); + positionAddr += 4; + } + + @Override + public void writeFloat(long offset, float v) { + UNSAFE.putFloat(startAddr + offset, v); + } + + @Override + public void writeDouble(double v) { + UNSAFE.putDouble(positionAddr, v); + positionAddr += 8; + } + + @Override + public void writeDouble(long offset, double v) { + UNSAFE.putDouble(startAddr + offset, v); + } + + @Override + public void readObject(Object object, int start, int end) { + int len = end - start; + for (; len >= 8; len -= 8) { + UNSAFE.putLong(object, (long) start, UNSAFE.getLong(positionAddr)); + positionAddr += 8; + start += 8; + } + for (; len > 0; len--) { + UNSAFE.putByte(object, (long) start, UNSAFE.getByte(positionAddr)); + positionAddr++; + start++; + } + } + + @Override + public void writeObject(Object object, int start, int end) { + int len = end - start; + for (; len >= 8; len -= 8) { + UNSAFE.putLong(positionAddr, UNSAFE.getLong(object, (long) start)); + positionAddr += 8; + start += 8; + } + for (; len > 0; len--) { + UNSAFE.putByte(positionAddr, UNSAFE.getByte(object, (long) start)); + positionAddr++; + start++; + } + } + + @Override + public long position() { + return (positionAddr - startAddr); + } + + @Override + public void position(long position) { + this.positionAddr = startAddr + position; + } + + @Override + public long capacity() { + return (limitAddr - startAddr); + } + + @Override + public long remaining() { + return (limitAddr - positionAddr); + } + + @NotNull + @Override + public ByteOrder byteOrder() { + return ByteOrder.nativeOrder(); + } + + @Override + public void checkEndOfBuffer() throws IndexOutOfBoundsException { + if (position() > capacity()) + throw new IndexOutOfBoundsException("position is beyond the end of the buffer " + position() + " > " + capacity()); + } + + public long startAddr() { + return startAddr; + } + + public long positionAddr() { + return positionAddr; + } + + public void positionAddr(long positionAddr) { + this.positionAddr = positionAddr; + } + + public long limitAddr() { + return limitAddr; + } +} diff --git a/lang/src/main/java/net/openhft/lang/io/RandomDataInput.java b/lang/src/main/java/net/openhft/lang/io/RandomDataInput.java new file mode 100755 index 0000000..3581b0f --- /dev/null +++ b/lang/src/main/java/net/openhft/lang/io/RandomDataInput.java @@ -0,0 +1,868 @@ +/* + * 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.io; + +import org.jetbrains.annotations.NotNull; +import org.jetbrains.annotations.Nullable; + +import java.io.ObjectInput; +import java.nio.ByteBuffer; +import java.util.Collection; +import java.util.Map; +import java.util.RandomAccess; + +/** + * @author peter.lawrey + */ +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 + * 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. + * + * @param bytes the buffer into which the data is read. + */ + @Override + 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 + * 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>. + * + * @param bytes the buffer into which the data is read. + * @param off an int specifying the offset into the data. + * @param len an int specifying the number of bytes to read. + */ + @Override + void readFully(@NotNull byte[] bytes, 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 + * conditions; reaching end of file before <code>n</code> bytes have been skipped is only one possibility. This + * method never throws an <code>EOFException</code>. The actual number of bytes skipped is returned. + * + * @param n the number of bytes to be skipped. + * @return the number of bytes actually skipped. + */ + @Override + int skipBytes(int n); + + /** + * Reads one input byte and returns <code>true</code> if that byte is nonzero, <code>false</code> if that byte is + * zero. This method is suitable for reading the byte written by the <code>writeBoolean</code> method of interface + * <code>DataOutput</code>. + * + * @return the <code>boolean</code> value read. + */ + @Override + boolean readBoolean(); + + /** + * Reads one input byte and returns <code>true</code> if that byte is nonzero, <code>false</code> if that byte is + * zero. This method is suitable for reading the byte written by the <code>writeBoolean</code> method of interface + * <code>RandomDataOutput</code>. + * + * @param offset to read byte translated into a boolean + * @return the <code>boolean</code> value read. + */ + boolean readBoolean(long offset); + + /** + * Reads and returns one input byte. The byte is treated as a signed value in the range <code>-128</code> through + * <code>127</code>, inclusive. This method is suitable for reading the byte written by the <code>writeByte</code> + * method of interface <code>DataOutput</code>. + * + * @return the 8-bit value read. + */ + @Override + byte readByte(); + + /** + * Reads and returns one input byte. The byte is treated as a signed value in the range <code>-128</code> through + * <code>127</code>, inclusive. This method is suitable for reading the byte written by the <code>writeByte</code> + * method of interface <code>RandomDataOutput</code>. + * + * @param offset of byte to read. + * @return the 8-bit value read. + */ + byte readByte(long offset); + + /** + * Reads one input byte, zero-extends it to type <code>int</code>, and returns the result, which is therefore in the + * range <code>0</code> through <code>255</code>. This method is suitable for reading the byte written by the + * <code>writeByte</code> method of interface <code>DataOutput</code> if the argument to <code>writeByte</code> was + * intended to be a value in the range <code>0</code> through <code>255</code>. + * + * @return the unsigned 8-bit value read. + */ + @Override + int readUnsignedByte(); + + /** + * Reads one input byte, zero-extends it to type <code>int</code>, and returns the result, which is therefore in the + * range <code>0</code> through <code>255</code>. This method is suitable for reading the byte written by the + * <code>writeByte</code> method of interface <code>RandomDataOutput</code> if the argument to + * <code>writeByte</code> was intended to be a value in the range <code>0</code> through <code>255</code>. + * + * @param offset of byte to read + * @return the unsigned 8-bit value read. + */ + int readUnsignedByte(long offset); + + /** + * 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)) + * </code></pre> + * This method is suitable for reading the bytes written by the <code>writeShort</code> method of interface + * <code>DataOutput</code>. + * + * @return the 16-bit value read. + */ + @Override + short readShort(); + + /** + * 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)) + * </code></pre> + * This method is suitable for reading the bytes written by the <code>writeShort</code> method of interface + * <code>RandomDataOutput</code>. + * + * @param offset of short to read. + * @return the 16-bit value read. + */ + short readShort(long offset); + + /** + * 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> + * (((a & 0xff) << 8) | (b & 0xff)) + * </code></pre> + * This method is suitable for reading the bytes written by the <code>writeUnsignedShort</code> method of interface + * <code>DataOutput</code> if the argument to <code>writeUnsignedShort</code> was intended to be a value in the + * range <code>0</code> through <code>65535</code>. + * + * @return the unsigned 16-bit value read. + */ + @Override + int readUnsignedShort(); + + /** + * 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> + * (((a & 0xff) << 8) | (b & 0xff)) + * </code></pre> + * This method is suitable for reading the bytes written by the <code>writeShort</code> method of interface + * <code>RandomDataOutput</code> if the argument to <code>writeUnsignedShort</code> was intended to be a value in + * the range <code>0</code> through <code>65535</code>. + * + * @param offset of short to read. + * @return the unsigned 16-bit value read. + */ + int readUnsignedShort(long offset); + + /** + * 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 + * <code>RandomDataOutput</code>. + * + * @return the 16-bit value read. + */ + short readCompactShort(); + + /** + * 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 + * interface <code>RandomDataOutput</code>. + * + * @return the unsigned 16-bit value read. + */ + int readCompactUnsignedShort(); + + /** + * 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)) + * </code></pre> + * This method is suitable for reading bytes written by the <code>writeChar</code> method of interface + * <code>DataOutput</code>. + * + * @return the <code>char</code> value read. + */ + @Override + char readChar(); + + /** + * 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)) + * </code></pre> + * This method is suitable for reading bytes written by the <code>writeChar</code> method of interface + * <code>RandomDataOutput</code>. + * + * @param offset of the char to read. + * @return the <code>char</code> value read. + */ + char readChar(long offset); + + /** + * 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> + * <code> + * ((((a & 0xff) << 24) | ((b & 0xff) << 16) | ((c & 0xff) << 8))) >> 8 + * </code></pre> + * This method is suitable for reading bytes written by the <code>writeInt24</code> method of interface + * <code>RandomDataOutput</code>. + * + * @return the <code>int</code> value read. + */ + int readInt24(); + + /** + * 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> + * <code> + * ((((a & 0xff) << 24) | ((b & 0xff) << 16) | ((c & 0xff) << 8))) >> 8 + * </code></pre> + * This method is suitable for reading bytes written by the <code>writeInt24</code> method of interface + * <code>RandomDataOutput</code>. + * + * @param offset to read from + * @return the <code>int</code> value read. + */ + int readInt24(long offset); + + /** + * 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> + * <code> + * (((a & 0xff) << 24) | ((b & 0xff) << 16) | ((c & 0xff) << 8) | (d & 0xff)) + * </code></pre> + * This method is suitable for reading bytes written by the <code>writeInt</code> method of interface + * <code>DataOutput</code>. + * + * @return the <code>int</code> value read. + */ + @Override + int readInt(); + + /** + * 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> + * <code> + * (((a & 0xff) << 24) | ((b & 0xff) << 16) | ((c & 0xff) << 8) | (d & 0xff)) + * </code></pre> + * This method is suitable for reading bytes written by the <code>writeInt</code> method of interface + * <code>RandomDataOutput</code>. + * + * @param offset to read from + * @return the <code>int</code> value read. + */ + int readInt(long offset); + + /** + * 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> + * <code> + * (((a & 0xff) << 24) | ((b & 0xff) << 16) | ((c & 0xff) << 8) | (d & 0xff)) + * </code></pre> + * This method is suitable for reading bytes written by the <code>writeOrderedInt</code> or + * <code>writeVolatileInt</code> method of interface <code>RandomDataOutput</code>. + * + * @return the <code>int</code> value read. + */ + int readVolatileInt(); + + /** + * 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> + * <code> + * (((a & 0xff) << 24) | ((b & 0xff) << 16) | ((c & 0xff) << 8) | (d & 0xff)) + * </code></pre> + * This method is suitable for reading bytes written by the <code>writeOrderedInt</code> or + * <code>writeVolatileInt</code> method of interface <code>RandomDataOutput</code>. + * + * @param offset to read from + * @return the <code>int</code> value read. + */ + int readVolatileInt(long offset); + + /** + * 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> + * <code> + * ((((long) a & 0xff) << 24) | ((b & 0xff) << 16) | ((c & 0xff) << 8) | (d & + * 0xff)) + * </code></pre> + * This method is suitable for reading bytes written by the <code>writeUnsignedInt</code> method of interface + * <code>RandomDataOutput</code>. + * + * @return the unsigned <code>int</code> value read. + */ + long readUnsignedInt(); + + /** + * 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> + * <code> + * ((((long) a & 0xff) << 24) | ((b & 0xff) << 16) | ((c & 0xff) << 8) | (d & + * 0xff)) + * </code></pre> + * This method is suitable for reading bytes written by the <code>writeUnsignedInt</code> method of interface + * <code>RandomDataOutput</code>. + * + * @param offset to read from + * @return the unsigned <code>int</code> value read. + */ + long readUnsignedInt(long offset); + + /** + * 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; 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 + * <code>RandomDataOutput</code>. + * + * @return the 32-bit value read. + */ + int readCompactInt(); + + /** + * 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 + * interface <code>RandomDataOutput</code>. + * + * @return the unsigned 32-bit value read. + */ + long readCompactUnsignedInt(); + + /** + * 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> + * (((long)(a & 0xff) << 56) | + * ((long)(b & 0xff) << 48) | + * ((long)(c & 0xff) << 40) | + * ((long)(d & 0xff) << 32) | + * ((long)(e & 0xff) << 24) | + * ((long)(f & 0xff) << 16) | + * ((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 + * <code>DataOutput</code>. + * + * @return the <code>long</code> value read. + */ + @Override + long readLong(); + + /** + * 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> + * (((long)(a & 0xff) << 56) | + * ((long)(b & 0xff) << 48) | + * ((long)(c & 0xff) << 40) | + * ((long)(d & 0xff) << 32) | + * ((long)(e & 0xff) << 24) | + * ((long)(f & 0xff) << 16) | + * ((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 + * <code>RandomDataOutput</code>. + * + * @param offset of the long to read + * @return the <code>long</code> value read. + */ + long readLong(long offset); + + /** + * 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 + * bytes read on big endian machines, and the opposite on little endian machines. The value returned is: + * <p><pre> <code> + * (((long)(a & 0xff) << 56) | + * ((long)(b & 0xff) << 48) | + * ((long)(c & 0xff) << 40) | + * ((long)(d & 0xff) << 32) | + * ((long)(e & 0xff) << 24) | + * ((long)(f & 0xff) << 16) | + * ((long)(g & 0xff) << 8) | + * ((long)(h & 0xff))) + * </code></pre> + * <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. + */ + long readVolatileLong(); + + /** + * 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 + * bytes read on big endian machines, and the opposite on little endian machines. The value returned is: + * <p><pre> <code> + * (((long)(a & 0xff) << 56) | + * ((long)(b & 0xff) << 48) | + * ((long)(c & 0xff) << 40) | + * ((long)(d & 0xff) << 32) | + * ((long)(e & 0xff) << 24) | + * ((long)(f & 0xff) << 16) | + * ((long)(g & 0xff) << 8) | + * ((long)(h & 0xff))) + * </code></pre> + * <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 + * @return the <code>long</code> value read. + */ + long readVolatileLong(long offset); + + /** + * 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> + * (((long)(a & 0xff) << 56) | + * ((long)(b & 0xff) << 48) | + * ((long)(c & 0xff) << 40) | + * ((long)(d & 0xff) << 32) | + * ((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 + * <code>RandomDataOutput</code>. + * + * @return the <code>long</code> value read. + */ + long readInt48(); + + /** + * 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> + * (((long)(a & 0xff) << 56) | + * ((long)(b & 0xff) << 48) | + * ((long)(c & 0xff) << 40) | + * ((long)(d & 0xff) << 32) | + * ((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 + * <code>RandomDataOutput</code>. + * + * @param offset of the long to read + * @return the <code>long</code> value read. + */ + long readInt48(long offset); + + /** + * 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 + * <code>RandomDataOutput</code>. + * + * @return the 64-bit value read. + */ + long readCompactLong(); + + /** + * Reads between one and ten bytes with are stop encoded with support for negative numbers + * <p><pre><code> + * long l = 0, b; + * int count = 0; + * while ((b = readByte()) < 0) { + * l |= (b & 0x7FL) << count; + * count += 7; + * } + * if (b == 0 && count > 0) + * return ~l; + * return l | (b << count); + * </code></pre> + * + * @return a stop bit encoded number as a long. + */ + long readStopBit(); + + /** + * Reads four input bytes and returns a <code>float</code> value. It does this by first constructing an + * <code>int</code> value in exactly the manner of the <code>readInt</code> method, then converting this + * <code>int</code> value to a <code>float</code> in exactly the manner of the method + * <code>Float.intBitsToFloat</code>. This method is suitable for reading bytes written by the + * <code>writeFloat</code> method of interface <code>DataOutput</code>. + * + * @return the <code>float</code> value read. + */ + @Override + float readFloat(); + + /** + * Reads four input bytes and returns a <code>float</code> value. It does this by first constructing an + * <code>int</code> value in exactly the manner of the <code>readInt</code> method, then converting this + * <code>int</code> value to a <code>float</code> in exactly the manner of the method + * <code>Float.intBitsToFloat</code>. This method is suitable for reading bytes written by the + * <code>writeFloat</code> method of interface <code>DataOutput</code>. + * + * @param offset to read from + * @return the <code>float</code> value read. + */ + float readFloat(long offset); + + /** + * 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>. + * + * @param offset to read from + * @return the <code>int</code> value read. + */ + float readVolatileFloat(long offset); + + /** + * Reads eight input bytes and returns a <code>double</code> value. It does this by first constructing a + * <code>long</code> value in exactly the manner of the <code>readLong</code> method, then converting this + * <code>long</code> value to a <code>double</code> in exactly the manner of the method + * <code>Double.longBitsToDouble</code>. This method is suitable for reading bytes written by the + * <code>writeDouble</code> method of interface <code>DataOutput</code>. + * + * @return the <code>double</code> value read. + */ + @Override + double readDouble(); + + /** + * Reads eight input bytes and returns a <code>double</code> value. It does this by first constructing a + * <code>long</code> value in exactly the manner of the <code>readLong</code> method, then converting this + * <code>long</code> value to a <code>double</code> in exactly the manner of the method + * <code>Double.longBitsToDouble</code>. This method is suitable for reading bytes written by the + * <code>writeDouble</code> method of interface <code>DataOutput</code>. + * + * @param offset to read from + * @return the <code>double</code> value read. + */ + double readDouble(long offset); + + /** + * Reads the first four bytes as readFloat(). If this is Float.NaN, the next eight bytes are read as readDouble() + * + * @return the <code>double</code> value read. + */ + double readCompactDouble(); + + /** + * 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>. + * + * @param offset to read from + * @return the <code>int</code> value read. + */ + double readVolatileDouble(long offset); + + /** + * Reads the next line of text from the input stream. It reads successive bytes, converting each byte separately + * 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, + * 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 + * that is discarded also; reading then ceases. If end of file is encountered before either of the characters + * <code>'\n'</code> and <code>'\r'</code> is encountered, reading ceases. Once reading has ceased, a + * <code>String</code> is returned that contains all the characters read and not discarded, taken in order. Note + * that every character in this string will have a value less than <code>\u0100</code>, that is, + * <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. + */ + @Override + @Nullable + String readLine(); + + /** + * 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 + * <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 + * <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 + * <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> + * <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> + * <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 + * thrown. + * <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 + * for reading by this method. + * + * @return a Unicode string. + * @throws IllegalStateException if the bytes do not represent a valid modified UTF-8 encoding of a string. + */ + @Override + @NotNull + String readUTF(); + + /** + * The same as readUTF() except the length is stop bit encoded. This saves one byte for strings shorter than 128 + * chars. <code>null</code> values are also supported + * + * @return a Unicode string or <code>null</code> if <code>writeUTFΔ(null)</code> was called + */ + @Nullable + String readUTFΔ(); + + /** + * The same as readUTFΔ() except an offset and maximum length is given. + * + * @param offset to read from + * @return a Unicode string or <code>null</code> if <code>writeUTFΔ(null)</code> was called + * @throws IllegalStateException if the length to be read is out of range. + */ + @Nullable + String readUTFΔ(long offset) throws IllegalStateException; + + /** + * The same as readUTFΔ() except the chars are copied to a truncated StringBuilder. + * + * @return <code>true</code> if there was a String, or <code>false</code> if it was <code>null</code> + */ + boolean readUTFΔ(@NotNull StringBuilder stringBuilder); + + /** + * Copy bytes into a ByteBuffer to the minimum of the length <code>remaining()</code> in the ByteBuffer or the + * Excerpt. + * + * @param bb to copy into + */ + void read(@NotNull ByteBuffer bb); + + /** + * 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> + * interface + * + * @param eClass to decode the String as + * @return the decoded value. <code>null</code> with be return if null was written. + */ + @Nullable + <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> + * interface + * + * @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> + * interface + * + * @param map to populate + * @return the map + */ + <K, V> Map<K, V> readMap(@NotNull Map<K, V> map, @NotNull Class<K> kClass, @NotNull Class<V> vClass); + + // ObjectInput + + /** + * Read and return an object. The class that implements this interface defines where the object is "read" from. + * + * @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. + */ + @Nullable + @Override + Object readObject() throws IllegalStateException; + + /** + * Read and return an object. The class that implements this interface defines where the object is "read" from. + * + * @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. + * @throws ClassCastException The class cannot be cast or converted to the type given. + */ + @Nullable + <T> T readObject(Class<T> tClass) throws IllegalStateException; + + /** + * Reads a byte of data. This method will block if no input is available. + * + * @return the byte read, or -1 if the end of the stream is reached. + */ + @Override + int read(); + + /** + * Reads into an array of bytes. This method will block until some input is available. + * + * @param bytes the buffer into which the data is read + * @return the actual number of bytes read, -1 is returned when the end of the stream is reached. + */ + @Override + int read(@NotNull byte[] bytes); + + /** + * Reads into an array of bytes. This method will block until some input is available. + * + * @param bytes the buffer into which the data is read + * @param off the start offset of the data + * @param len the maximum number of bytes read + * @return the actual number of bytes read, -1 is returned when the end of the stream is reached. + */ + @Override + int read(@NotNull byte[] bytes, int off, int len); + + /** + * Read the object from start to end bytes + * + * @param object to read into + * @param start byte inclusive + * @param end byte exclusive + */ + void readObject(Object object, int start, int end); + + /** + * Skips n bytes of input. + * + * @param n the number of bytes to be skipped + * @return the actual number of bytes skipped. + */ + @Override + long skip(long n); + + /** + * @return remaining() or Integer.MAX_VALUE if larger. + */ + @Override + int available(); + + /** + * Finishes the excerpt entry if not finished() + */ + @Override + void close(); +} diff --git a/lang/src/main/java/net/openhft/lang/io/RandomDataOutput.java b/lang/src/main/java/net/openhft/lang/io/RandomDataOutput.java new file mode 100755 index 0000000..92a1a4c --- /dev/null +++ b/lang/src/main/java/net/openhft/lang/io/RandomDataOutput.java @@ -0,0 +1,819 @@ +/* + * 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.io; + +import org.jetbrains.annotations.NotNull; +import org.jetbrains.annotations.Nullable; + +import java.io.ObjectOutput; +import java.nio.ByteBuffer; +import java.util.Collection; +import java.util.Map; +import java.util.RandomAccess; + +/** + * @author peter.lawrey + */ +public interface RandomDataOutput extends ObjectOutput, RandomAccess, BytesCommon { + /** + * Copy from one Bytes to another. Copied from the start to the current position. + * + * @param bytes to copy from + * @deprecated Use write(BytesCommon bytes, long position, long length) instead. + */ + @Deprecated + void writeStartToPosition(Bytes bytes); + + /** + * Writes to the output stream the eight low-order bits of the argument <code>b</code>. The 24 high-order bits of + * <code>b</code> are ignored. + * + * @param b the byte to be written. + */ + @Override + void write(int b); + + /** + * Writes to the output stream the eight low- order bits of the argument <code>v</code>. The 24 high-order bits of + * <code>v</code> are ignored. (This means that <code>writeByte</code> does exactly the same thing as + * <code>write</code> for an integer argument.) The byte written by this method may be read by the + * <code>readByte</code> method of interface <code>DataInput</code>, which will then return a <code>byte</code> + * equal to <code>(byte)v</code>. + * + * @param v the byte value to be written. + */ + @Override + void writeByte(int v); + + /** + * Writes to the output stream the eight low- order bits of the argument <code>v</code>. The 24 high-order bits of + * <code>v</code> are ignored. (This means that <code>writeByte</code> does exactly the same thing as + * <code>write</code> for an integer argument.) The byte written by this method may be read by the + * <code>readUnsignedByte</code> method of interface <code>DataInput</code>, which will then return a + * <code>byte</code> equal to <code>(byte)v</code>. + * + * @param v the byte value to be written. + */ + void writeUnsignedByte(int v); + + /** + * Writes to the output stream the eight low-order bits of the argument <code>b</code>. The 24 high-order bits of + * <code>b</code> are ignored. + * + * @param offset to write byte + * @param b the byte to be written. + */ + void writeByte(long offset, int b); + + /** + * Writes to the output stream the eight low- order bits of the argument <code>v</code>. The 24 high-order bits of + * <code>v</code> are ignored. (This means that <code>writeByte</code> does exactly the same thing as + * <code>write</code> for an integer argument.) The byte written by this method may be read by the + * <code>readUnsignedByte</code> method of interface <code>DataInput</code>, which will then return a + * <code>byte</code> equal to <code>v & 0xFF</code>. + * + * @param offset to write byte + * @param v the unsigned byte value to be written. + */ + void writeUnsignedByte(long offset, int v); + + /** + * Writes to the output stream all the bytes in array <code>bytes</code>. 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 written. Otherwise, the byte <code>bytes[0]</code> is written first, then <code>bytes[1]</code>, and so + * on; the last byte written is <code>bytes[bytes.length-1]</code>. + * + * @param bytes the data. + */ + @Override + void write(byte[] bytes); + + /** + * Writes to the output stream all the bytes in array <code>bytes</code>. 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 written. Otherwise, the byte <code>bytes[0]</code> is written first, then <code>bytes[1]</code>, and so + * on; the last byte written is <code>bytes[bytes.length-1]</code>. + * + * @param offset to be written + * @param bytes the data. + */ + void write(long offset, byte[] 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 + * negative, or <code>len</code> is negative, or <code>off+len</code> is greater than the length of the array + * <code>bytes</code>, then an <code>IndexOutOfBoundsException</code> is thrown. If <code>len</code> is zero, then + * no bytes are written. Otherwise, the byte <code>bytes[off]</code> is written first, then + * <code>bytes[off+1]</code>, and so on; the last byte written is <code>bytes[off+len-1]</code>. + * + * @param bytes the data. + * @param off the start offset in the data. + * @param len the number of bytes to write. + */ + @Override + void write(byte[] bytes, 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 + * <code>(byte)0</code> is written. The byte written by this method may be read by the <code>readBoolean</code> + * method of interface <code>DataInput</code>, which will then return a <code>boolean</code> equal to + * <code>v</code>. + * + * @param v the boolean to be written. + */ + @Override + void writeBoolean(boolean v); + + /** + * 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 + * <code>(byte)0</code> is written. The byte written by this method may be read by the <code>readBoolean</code> + * method of interface <code>DataInput</code>, which will then return a <code>boolean</code> equal to + * <code>v</code>. + * + * @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> + * <pre><code> + * (byte)(0xff & (v >> 8)) + * (byte)(0xff & v) + * </code> </pre> <p> + * The bytes written by this method may be read by the <code>readShort</code> method of interface + * <code>DataInput</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. + */ + @Override + void writeShort(int 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> + * <pre><code> + * (byte)(0xff & (v >> 8)) + * (byte)(0xff & v) + * </code> </pre> <p> + * The bytes written by this method may be read by the <code>readShort</code> method of interface + * <code>DataInput</code> , which will then return a <code>short</code> equal to <code>(short)v</code>. + * + * @param offset to be written to + * @param v the <code>short</code> value to be written. + */ + void writeShort(long offset, int 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> + * <pre><code> + * (byte)(0xff & (v >> 8)) + * (byte)(0xff & v) + * </code> </pre> <p> + * The bytes written by this method may be read by the <code>readUnsignedShort</code> method of interface + * <code>RandomDataInput</code> , which will then return a <code>short</code> equal to <code>(short)v</code>. + * + * @param v the unsigned <code>short</code> value to be written. + */ + void writeUnsignedShort(int 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> + * <pre><code> + * (byte)(0xff & (v >> 8)) + * (byte)(0xff & v) + * </code> </pre> <p> + * The bytes written by this method may be read by the <code>readShort</code> method of interface + * <code>RandomDataInput</code> , which will then return a <code>short</code> equal to <code>(short)v</code>. + * + * @param offset to be written to + * @param v the unsigned <code>short</code> value to be written. + */ + void writeUnsignedShort(long offset, int v); + + /** + * 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 + * <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. + */ + void writeCompactShort(int v); + + /** + * 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 + * <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. + */ + void writeCompactUnsignedShort(int v); + + /** + * 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> + * (byte)(0xff & (v >> 8)) + * (byte)(0xff & v) + * </code></pre><p> + * The bytes written by this method may be read by the <code>readChar</code> method of interface + * <code>DataInput</code> , which will then return a <code>char</code> equal to <code>(char)v</code>. + * + * @param v the <code>char</code> value to be written. + */ + @Override + void writeChar(int v); + + /** + * 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> + * (byte)(0xff & (v >> 8)) + * (byte)(0xff & v) + * </code></pre><p> + * The bytes written by this method may be read by the <code>readChar</code> method of interface + * <code>DataInput</code> , which will then return a <code>char</code> equal to <code>(char)v</code>. + * + * @param offset to be written to + * @param v the <code>char</code> value to be written. + */ + void writeChar(long offset, int v); + + /** + * 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> + * (byte)(0xff & (v >> 16)) + * (byte)(0xff & (v >>    8)) + * (byte)(0xff & v) + * </code></pre><p> + * The bytes written by this method may be read by the <code>readInt24</code> method of interface + * <code>RandomDataInput</code> , which will then return an <code>int</code> equal to <code>v</code>. + * + * @param v the <code>int</code> value to be written. + */ + void writeInt24(int v); + + /** + * 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> + * (byte)(0xff & (v >> 16)) + * (byte)(0xff & (v >>    8)) + * (byte)(0xff & v) + * </code></pre><p> + * The bytes written by this method may be read by the <code>readInt24</code> method of interface + * <code>RandomDataInput</code> , which will then return an <code>int</code> equal to <code>v</code>. + * + * @param offset to be written to + * @param v the <code>int</code> value to be written. + */ + void writeInt24(long offset, int v); + + /** + * 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> + * (byte)(0xff & (v >> 24)) + * (byte)(0xff & (v >> 16)) + * (byte)(0xff & (v >>    8)) + * (byte)(0xff & v) + * </code></pre><p> + * The bytes written by this method may be read by the <code>readInt</code> method of interface + * <code>DataInput</code> , which will then return an <code>int</code> equal to <code>v</code>. + * + * @param v the <code>int</code> value to be written. + */ + @Override + void writeInt(int v); + + /** + * 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> + * (byte)(0xff & (v >> 24)) + * (byte)(0xff & (v >> 16)) + * (byte)(0xff & (v >>    8)) + * (byte)(0xff & v) + * </code></pre><p> + * The bytes written by this method may be read by the <code>readInt</code> method of interface + * <code>DataInput</code> , which will then return an <code>int</code> equal to <code>v</code>. + * + * @param offset to be written to + * @param v the <code>int</code> value to be written. + */ + void writeInt(long offset, int v); + + /** + * 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> + * (byte)(0xff & (v >> 24)) + * (byte)(0xff & (v >> 16)) + * (byte)(0xff & (v >>    8)) + * (byte)(0xff & v) + * </code></pre><p> + * The bytes written by this method may be read by the <code>readUnsignedInt</code> method of interface + * <code>RandomDataInput</code> , which will then return an <code>long</code> equal to <code>v & + * 0xFFFFFFFF</code>. + * + * @param v the <code>int</code> value to be written. + */ + void writeUnsignedInt(long v); + + /** + * 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> + * (byte)(0xff & (v >> 24)) + * (byte)(0xff & (v >> 16)) + * (byte)(0xff & (v >>    8)) + * (byte)(0xff & v) + * </code></pre><p> + * The bytes written by this method may be read by the <code>readUnsignedInt</code> method of interface + * <code>RandomDataInput</code> , which will then return an <code>long</code> equal to <code>v & + * 0xFFFFFFFF</code>. + * + * @param offset to be written to + * @param v the <code>int</code> value to be written. + */ + void writeUnsignedInt(long offset, long v); + + /** + * 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 + * <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. + */ + void writeCompactInt(int v); + + /** + * 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 + * <code>RandomDataInput</code> , which will then return a <code>int</code> equal to <code>v & + * 0xFFFFFFFF</code>. + * + * @param v the <code>short</code> value to be written. + */ + void writeCompactUnsignedInt(long v); + + /** + * 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 + * same time. + * + * @param v value to write + */ + void writeOrderedInt(int v); + + /** + * 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 + * visible to other threads at the same time. + * + * @param offset to write to + * @param v value to write + */ + void writeOrderedInt(long offset, int v); + + /** + * 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 compareAndSwapInt(long offset, int expected, int x); + + /** + * Atomically adds the given value to the current value. + * + * @param offset of the int value to use. + * @param delta the value to add + * @return the previous value + */ + int getAndAdd(long offset, int delta); + + /** + * Atomically adds the given value to the current value. + * + * @param offset of the int value to use. + * @param delta the value to add + * @return the updated value + */ + int addAndGetInt(long offset, int delta); + + /** + * 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> + * (byte)(0xff & (v >> 40)) + * (byte)(0xff & (v >> 32)) + * (byte)(0xff & (v >> 24)) + * (byte)(0xff & (v >> 16)) + * (byte)(0xff & (v >> 8)) + * (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) + * - 1)</code>. + * + * @param v the <code>long</code> value to be written. + */ + void writeInt48(long v); + + /** + * 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> + * (byte)(0xff & (v >> 40)) + * (byte)(0xff & (v >> 32)) + * (byte)(0xff & (v >> 24)) + * (byte)(0xff & (v >> 16)) + * (byte)(0xff & (v >> 8)) + * (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) + * - 1)</code>. + * + * @param offset to be written to + * @param v the <code>long</code> value to be written. + */ + void writeInt48(long offset, long v); + + /** + * 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> + * (byte)(0xff & (v >> 56)) + * (byte)(0xff & (v >> 48)) + * (byte)(0xff & (v >> 40)) + * (byte)(0xff & (v >> 32)) + * (byte)(0xff & (v >> 24)) + * (byte)(0xff & (v >> 16)) + * (byte)(0xff & (v >> 8)) + * (byte)(0xff & v) + * </code></pre><p> + * The bytes written by this method may be read by the <code>readLong</code> method of interface + * <code>DataInput</code> , which will then return a <code>long</code> equal to <code>v</code>. + * + * @param v the <code>long</code> value to be written. + */ + @Override + void writeLong(long v); + + /** + * 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> + * (byte)(0xff & (v >> 56)) + * (byte)(0xff & (v >> 48)) + * (byte)(0xff & (v >> 40)) + * (byte)(0xff & (v >> 32)) + * (byte)(0xff & (v >> 24)) + * (byte)(0xff & (v >> 16)) + * (byte)(0xff & (v >> 8)) + * (byte)(0xff & v) + * </code></pre><p> + * The bytes written by this method may be read by the <code>readLong</code> method of interface + * <code>DataInput</code> , which will then return a <code>long</code> equal to <code>v</code>. + * + * @param offset to be written to + * @param v the <code>long</code> value to be written. + */ + void writeLong(long offset, long v); + + /** + * 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 + * <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. + */ + void writeCompactLong(long v); + + /** + * 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 + * same time. + * + * @param v value to write + */ + void writeOrderedLong(long v); + + /** + * 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 + * same time. + * + * @param offset to be written to + * @param v value to write + */ + void writeOrderedLong(long offset, long v); + + /** + * 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 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. + */ + void writeStopBit(long n); + + /** + * Writes a <code>float</code> value, which is comprised of four bytes, to the output stream. It does this as if it + * first converts this <code>float</code> value to an <code>int</code> in exactly the manner of the + * <code>Float.floatToIntBits</code> method and then writes the <code>int</code> value in exactly the manner of the + * <code>writeInt</code> method. The bytes written by this method may be read by the <code>readFloat</code> method + * of interface <code>DataInput</code>, which will then return a <code>float</code> equal to <code>v</code>. + * + * @param v the <code>float</code> value to be written. + */ + @Override + void writeFloat(float v); + + /** + * Writes a <code>float</code> value, which is comprised of four bytes, to the output stream. It does this as if it + * first converts this <code>float</code> value to an <code>int</code> in exactly the manner of the + * <code>Float.floatToIntBits</code> method and then writes the <code>int</code> value in exactly the manner of the + * <code>writeInt</code> method. The bytes written by this method may be read by the <code>readFloat</code> method + * of interface <code>DataInput</code>, which will then return a <code>float</code> equal to <code>v</code>. + * + * @param offset to write to + * @param v the <code>float</code> value to be written. + */ + void writeFloat(long offset, float v); + + /** + * 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 + * same time. + * + * @param v value to write + */ + void writeOrderedFloat(long offset, float v); + + /** + * Writes a <code>double</code> value, which is comprised of eight bytes, to the output stream. It does this as if + * it first converts this <code>double</code> value to a <code>long</code> in exactly the manner of the + * <code>Double.doubleToLongBits</code> method and then writes the <code>long</code> value in exactly the manner of + * the <code>writeLong</code> method. The bytes written by this method may be read by the <code>readDouble</code> + * method of interface <code>DataInput</code>, which will then return a <code>double</code> equal to + * <code>v</code>. + * + * @param v the <code>double</code> value to be written. + */ + @Override + void writeDouble(double v); + + /** + * Writes a <code>double</code> value, which is comprised of eight bytes, to the output stream. It does this as if + * it first converts this <code>double</code> value to a <code>long</code> in exactly the manner of the + * <code>Double.doubleToLongBits</code> method and then writes the <code>long</code> value in exactly the manner of + * the <code>writeLong</code> method. The bytes written by this method may be read by the <code>readDouble</code> + * method of interface <code>DataInput</code>, which will then return a <code>double</code> equal to + * <code>v</code>. + * + * @param offset to write to + * @param v the <code>double</code> value to be written. + */ + void writeDouble(long offset, double v); + + /** + * Writes four or twelve bytes as follow; + * <p><pre><code> + * if ((float) d == d) { + * writeFloat((float) d); + * } else { + * writeFloat(Float.NaN); + * writeDouble(d); + * } + * <p/> + * 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>. + * + * @param v the <code>double</code> value to be written. + */ + void writeCompactDouble(double v); + + /** + * 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 + * same time. + * + * @param v value to write + */ + void writeOrderedDouble(long offset, double v); + + /** + * Writes a string to the output stream. For every character in the string <code>s</code>, taken in order, one byte + * is written to the output stream. If <code>s</code> is <code>null</code>, a <code>NullPointerException</code> is + * thrown.<p> If <code>s.length</code> is zero, then no bytes are written. Otherwise, the character + * <code>s[0]</code> is written first, then <code>s[1]</code>, and so on; the last character written is + * <code>s[s.length-1]</code>. For each character, one byte is written, the low-order byte, in exactly the manner of + * the <code>writeByte</code> method . The high-order eight bits of each character in the string are ignored. + * + * @param s the string of bytes to be written. Cannot be null. + */ + @Override + void writeBytes(@NotNull String s); + + /** + * Writes every character in the string <code>s</code>, to the output stream, in order, two bytes per character. If + * <code>s</code> is <code>null</code>, a <code>NullPointerException</code> is thrown. If <code>s.length</code> is + * zero, then no characters are written. Otherwise, the character <code>s[0]</code> is written first, then + * <code>s[1]</code>, and so on; the last character written is <code>s[s.length-1]</code>. For each character, two + * 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. + */ + @Override + void writeChars(@NotNull String s); + + /** + * 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> + * <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> + * (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> + * (byte)(0xe0 | (0x0f & (c >> 12))) + * (byte)(0x80 | (0x3f & (c >> 6))) + * (byte)(0x80 | (0x3f & c)) + * </code></pre> <p> First, + * the total number of bytes needed to represent all the characters of <code>s</code> is calculated. If this number + * is larger than <code>65535</code>, then a <code>UTFDataFormatException</code> is thrown. Otherwise, this length + * is written to the output stream in exactly the manner of the <code>writeShort</code> method; after this, the + * one-, two-, or three-byte representation of each character in the string <code>s</code> is written.<p> The bytes + * written by this method may be read by the <code>readUTF</code> method of interface <code>DataInput</code> , which + * will then return a <code>String</code> equal to <code>s</code>. + * + * @param s the string value to be written. Cannot be null + */ + @Override + void writeUTF(@NotNull String s); + + /** + * Write the same encoding as <code>writeUTF</code> with the following changes. 1) The length is stop bit encoded + * 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. + */ + void writeUTFΔ(@Nullable CharSequence s); + + + /** + * Write the same encoding as <code>writeUTF</code> with the following changes. 1) The length is stop bit encoded + * i.e. one byte longer for short strings, but is not limited in length. 2) The string can be null. + * + * @param offset to write to + * @param maxSize maximum number of bytes to use + * @param s the string value to be written. Can be null. + * @throws IllegalStateException if the size is too large. + */ + void writeUTFΔ(long offset, int maxSize, @Nullable CharSequence s) throws IllegalStateException; + + /** + * Copies the contents of a ByteBuffer from the potision ot the limit. + * + * @param bb to copy. + */ + void write(@NotNull ByteBuffer bb); + + /** + * Write the object in a form which can be uniquely recreated by readEnum. This type of "enumerable objects" has + * the following constraints; 1) each object must have a one to one mapping with a toString() representation, 2) be + * immutable, 3) ideally appears more than once, 4) Must have a constructor which takes a single String or a + * <code>valueOf(String)</code> method. + * + * @param e to enumerate + * @param <E> element class + */ + <E> void writeEnum(@Nullable E e); + + /** + * 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 + * the type of each element. You can send the class first by using <code>writeEnum</code> of the element class + * + * @param list to be written + */ + <E> void writeList(@NotNull Collection<E> list); + + /** + * Write the keys and values of a Map of "enumerable objects" (See writeEnum). This writes the stop bit encoded + * 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 map to write out + */ + + <K, V> void writeMap(@NotNull Map<K, V> map); + + // ObjectOutput + + /** + * Write an object as either an "enumerable object" or a Serializable/Externalizable object using Java + * Serialization. Java Serialization is <i>much</i> slower but sometimes more convenient than using + * BytesMarshallable. + * + * @param object to write + */ + @Override + void writeObject(@Nullable Object object); + + /** + * Copy data from an Object from bytes start to end. + * + * @param object to copy from + * @param start first byte inclusive + * @param end last byte exclusive. + */ + void writeObject(Object object, int start, int end); + + /** + * Check the end of the stream has not overflowed. Otherwise this doesn't do anything. + */ + @Override + void flush(); + + /** + * The same as calling finish(); + */ + @Override + void close(); +} diff --git a/lang/src/main/java/net/openhft/lang/io/RandomDataUpdate.java b/lang/src/main/java/net/openhft/lang/io/RandomDataUpdate.java new file mode 100644 index 0000000..c019276 --- /dev/null +++ b/lang/src/main/java/net/openhft/lang/io/RandomDataUpdate.java @@ -0,0 +1,141 @@ +/* + * 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.io; + +/** + * This class supports updates which are a companion to RandomDataInput and RandomDataOutput + */ +public interface RandomDataUpdate { + + byte addByte(long offset, byte b); + + int addUnsignedByte(long offset, int i); + + short addShort(long offset, short s); + + int addUnsignedShort(long offset, int i); + + int addInt(long offset, int i); + + long addUnsignedInt(long offset, long i); + + long addLong(long offset, long i); + + float addFloat(long offset, float f); + + double addDouble(long offset, double d); + + int addAtomicInt(long offset, int i); + + long addAtomicLong(long offset, long l); + + float addAtomicFloat(long offset, float f); + + double addAtomicDouble(long offset, double d); + + /** + * Lock which uses 4 bytes. It store the lower 24 bits of the Thread Id and the re-entrant count as 8 bit. This + * means if you create more than 16 million threads you can get a collision, and if you try to re-enter 255 times + * you will get an ISE + * + * @param offset of the start of the 4-byte lock + * @return did it lock or not. + */ + boolean tryLockInt(long offset); + + /** + * Lock which uses 4 bytes. It store the lower 24 bits of the Thread Id and the re-entrant count as 8 bit. This + * means if you create more than 16 million threads you can get a collision, and if you try to re-enter 255 times + * you will get an ISE + * + * @param offset of the start of the 4-byte lock + * @param nanos to try to lock for + * @return did it lock or not. + */ + boolean tryLockNanosInt(long offset, long nanos); + + /** + * Lock which uses 4 bytes. It store the lower 24 bits of the Thread Id and the re-entrant count as 8 bit. This + * means if you create more than 16 million threads you can get a collision, and if you try to re-enter 255 times + * you will get an ISE + * + * @param offset of the start of the 4-byte lock + * @throws InterruptedException if interrupted + * @throws IllegalStateException if the thread tries to lock it 255 nested time (without an unlock) + */ + void busyLockInt(long offset) throws InterruptedException, IllegalStateException; + + /** + * Lock which uses 4 bytes. Unlock this It store the lower 24 bits of the Thread Id and the re-entrant count as 8 + * bit. This means if you create more than 16 million threads you can get a collision, and if you try to re-enter + * 255 times you will get an ISE + * + * @param offset of the start of the 4-byte lock + * @throws IllegalMonitorStateException if this thread doesn't hold the lock + */ + void unlockInt(long offset) throws IllegalMonitorStateException; + + /** + * 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 + * 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 + * + * @param offset of the start of the 8-byte lock + * @return did it lock or not. + */ + boolean tryLockLong(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 + * 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 + * + * @param offset of the start of the 8-byte lock + * @param nanos to try to lock for + * @return did it lock or not. + */ + boolean tryLockNanosLong(long offset, long nanos); + + /** + * 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 + * 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 + * + * @param offset of the start of the 8-byte lock + * @throws InterruptedException if interrupted + * @throws IllegalStateException if the thread tries to lock it 65535 nested time (without an unlock) + */ + void busyLockLong(long offset) throws InterruptedException, IllegalStateException; + + /** + * 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 + * 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 + * + * @param offset of the start of the 8-byte lock + * @throws IllegalMonitorStateException if this thread doesn't hold the lock + */ + void unlockLong(long offset) throws IllegalMonitorStateException; +} diff --git a/lang/src/main/java/net/openhft/lang/io/StopCharTester.java b/lang/src/main/java/net/openhft/lang/io/StopCharTester.java new file mode 100755 index 0000000..8f965e3 --- /dev/null +++ b/lang/src/main/java/net/openhft/lang/io/StopCharTester.java @@ -0,0 +1,35 @@ +/* + * 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.io; + +/** + * @author peter.lawrey + */ +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. + * + * @param ch to test, 0 should return true or throw an exception. + * @return if this byte is a stop character. + * @throws IllegalStateException if an invalid character like 0 was detected. + */ + boolean isStopChar(int ch) throws IllegalStateException; +} diff --git a/lang/src/main/java/net/openhft/lang/io/StopCharTesters.java b/lang/src/main/java/net/openhft/lang/io/StopCharTesters.java new file mode 100755 index 0000000..f711706 --- /dev/null +++ b/lang/src/main/java/net/openhft/lang/io/StopCharTesters.java @@ -0,0 +1,93 @@ +/* + * 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.io; + +import org.jetbrains.annotations.NotNull; + +/** + * @author peter.lawrey + */ +public enum StopCharTesters implements StopCharTester { + COMMA_STOP { + @Override + public boolean isStopChar(int ch) { + return ch < ' ' || ch == ','; + } + }, CONTROL_STOP { + @Override + public boolean isStopChar(int ch) { + return ch < ' '; + } + }, + SPACE_STOP { + @Override + public boolean isStopChar(int ch) { + return Character.isWhitespace(ch) || ch == 0; + } + }, + XML_TEXT { + @Override + public boolean isStopChar(int ch) { + return ch == '"' || ch == '<' || ch == '>' || ch == 0; + } + }, + FIX_TEXT { + @Override + public boolean isStopChar(int ch) { + return ch <= 1; + } + }; + + @NotNull + public static StopCharTester forChars(@NotNull CharSequence sequence) { + if (sequence.length() == 1) + return forChar(sequence.charAt(0)); + return new CSCSTester(sequence); + } + + @NotNull + public static StopCharTester forChar(char ch) { + return new CharCSTester(ch); + } + + static class CSCSTester implements StopCharTester { + @NotNull + private final String seperators; + + public CSCSTester(@NotNull CharSequence cs) { + seperators = cs.toString(); + } + + @Override + public boolean isStopChar(int ch) { + return seperators.indexOf(ch) >= 0; + } + } + + static class CharCSTester implements StopCharTester { + private final char ch; + + public CharCSTester(char ch) { + this.ch = ch; + } + + @Override + public boolean isStopChar(int ch) { + return this.ch == ch; + } + } +} 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 new file mode 100644 index 0000000..a92b316 --- /dev/null +++ b/lang/src/main/java/net/openhft/lang/io/serialization/BytesMarshallable.java @@ -0,0 +1,43 @@ +/* + * 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.io.serialization; + +import net.openhft.lang.io.Bytes; +import org.jetbrains.annotations.NotNull; + +/** + * Integrated marshaller for objects. + * + * @author peter.lawrey + * @see BytesMarshaller + */ +public interface BytesMarshallable { + /** + * read an object from bytes + * + * @param in to read from + * @throws IllegalStateException if the object could not be read. + */ + void readMarshallable(@NotNull Bytes in) throws IllegalStateException; + + /** + * write an object to bytes + * + * @param out to write to + */ + void writeMarshallable(@NotNull Bytes out); +} 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 new file mode 100644 index 0000000..d9a4fa2 --- /dev/null +++ b/lang/src/main/java/net/openhft/lang/io/serialization/BytesMarshaller.java @@ -0,0 +1,45 @@ +/* + * 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.io.serialization; + +import net.openhft.lang.io.Bytes; +import org.jetbrains.annotations.Nullable; + +/** + * External marshaller for classes. + * + * @author peter.lawrey + * @see BytesMarshallable + */ +public interface BytesMarshaller<E> { + /** + * write the object out as bytes. + * + * @param bytes to write to + * @param e to write + */ + void write(Bytes bytes, E e); + + /** + * Read bytes and obtain an object + * + * @param bytes to read + * @return the object + */ + @Nullable + E read(Bytes bytes); +} 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 new file mode 100644 index 0000000..87218b7 --- /dev/null +++ b/lang/src/main/java/net/openhft/lang/io/serialization/BytesMarshallerFactory.java @@ -0,0 +1,31 @@ +/* + * 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.io.serialization; + +import org.jetbrains.annotations.NotNull; + +/** + * @author peter.lawrey + */ +public interface BytesMarshallerFactory { + @NotNull + <E> BytesMarshaller<E> acquireMarshaller(@NotNull Class<E> eClass, boolean create); + + <E> BytesMarshaller<E> getMarshaller(byte code); + + <E> void addMarshaller(Class<E> eClass, BytesMarshaller<E> marshaller); +} 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 new file mode 100644 index 0000000..6ee1f87 --- /dev/null +++ b/lang/src/main/java/net/openhft/lang/io/serialization/CompactBytesMarshaller.java @@ -0,0 +1,27 @@ +/* + * 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.io.serialization; + +/** + * A BytesMarshaller with a byte code for the class. + */ +public interface CompactBytesMarshaller<E> extends BytesMarshaller<E> { + /** + * the code for this marshaller + */ + byte code(); +} diff --git a/lang/src/main/java/net/openhft/lang/io/serialization/RawCopier.java b/lang/src/main/java/net/openhft/lang/io/serialization/RawCopier.java new file mode 100644 index 0000000..eaf5760 --- /dev/null +++ b/lang/src/main/java/net/openhft/lang/io/serialization/RawCopier.java @@ -0,0 +1,104 @@ +/* + * 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.io.serialization; + +import net.openhft.lang.io.Bytes; + +import java.lang.reflect.Array; +import java.lang.reflect.Field; +import java.lang.reflect.Modifier; +import java.util.ArrayList; +import java.util.Collections; +import java.util.Comparator; +import java.util.List; + +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; + private final Class<T> tClass; + + public RawCopier(Class<T> tClass) { + this.tClass = tClass; + List<Field> fields = new ArrayList<Field>(); + addAllFields(fields, tClass); + Collections.sort(fields, new Comparator<Field>() { + @Override + public int compare(Field o1, Field o2) { + long off1 = UNSAFE.objectFieldOffset(o1); + long off2 = UNSAFE.objectFieldOffset(o2); + return Double.compare(off1, off2); + } + }); + start = (int) UNSAFE.objectFieldOffset(fields.get(0)); + Field lastField = null; + for (Field field : fields) { + if (Modifier.isTransient(field.getModifiers()) || !field.getType().isPrimitive()) + break; + lastField = field; + } + end = (int) UNSAFE.objectFieldOffset(lastField) + sizeOf(lastField.getType()); + + assert end > start : "end <= start, start: " + start + ", end: " + end; + } + + public static <T> RawCopier<T> copies(Class<T> tClass) { + return new RawCopier<T>(tClass); + } + + private static int sizeOf(Class<?> type) { + return UNSAFE.arrayIndexScale(Array.newInstance(type, 0).getClass()); + } + + public int start() { + return start; + } + + public int end() { + return end; + } + + public void toBytes(Object obj, Bytes bytes) { + bytes.writeObject(obj, start, end); + } + + public void fromBytes(Bytes bytes, Object obj) { + bytes.readObject(obj, start, end); + } + + public void copy(T from, T to) { + long i; + for (i = start; i < end - 7; i += 8) { + UNSAFE.putLong(to, i, UNSAFE.getLong(from, i)); + } + for (; i < end; i++) { + UNSAFE.putByte(to, i, UNSAFE.getByte(from, i)); + } + } + + private 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); + } + } +} 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 new file mode 100644 index 0000000..c6b5b2e --- /dev/null +++ b/lang/src/main/java/net/openhft/lang/io/serialization/direct/DirectSerializationFilter.java @@ -0,0 +1,29 @@ +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/DirectSerializationMetadata.java b/lang/src/main/java/net/openhft/lang/io/serialization/direct/DirectSerializationMetadata.java new file mode 100644 index 0000000..2fcd50c --- /dev/null +++ b/lang/src/main/java/net/openhft/lang/io/serialization/direct/DirectSerializationMetadata.java @@ -0,0 +1,89 @@ +package net.openhft.lang.io.serialization.direct; + +import net.openhft.lang.Jvm; + +import java.lang.reflect.Field; +import java.util.List; + +import static net.openhft.lang.io.NativeBytes.UNSAFE; + +public class DirectSerializationMetadata { + private static final int OBJECT_ALIGNMENT = 8; + 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); + 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); + + public static final class SerializationMetadata { + final long start; + final long length; + + SerializationMetadata(long start, long length) { + this.start = start; + this.length = length; + } + + @Override + public String toString() { + return String.format("SerializationMetadata: Start %s Length %s", start, length); + } + } + + public static SerializationMetadata extractMetadata(List<Field> fields) { + if (fields.isEmpty()) return EmptyObjectMetadata; + + Offsets offsets = minMaxOffsets(fields); + + long totalSize = OBJECT_HEADER_SIZE + offsets.max - offsets.min + 1; + return new SerializationMetadata(offsets.min, padToObjectAlignment(totalSize) - OBJECT_HEADER_SIZE); + } + + public static SerializationMetadata extractMetadataForPartialCopy(List<Field> fields) { + if (fields.isEmpty()) return EmptyObjectMetadata; + + Offsets offsets = minMaxOffsets(fields); + + Field lastField = fields.get(fields.size() - 1); + + return new SerializationMetadata(offsets.min, offsets.max + sizeOf(lastField) - OBJECT_HEADER_SIZE); + } + + private static Offsets minMaxOffsets(List<Field> fields) { + long minOffset = UNSAFE.objectFieldOffset(fields.get(0)); + long maxOffset = UNSAFE.objectFieldOffset(fields.get(fields.size() - 1)); + + return new Offsets(minOffset, maxOffset); + } + + static long padToObjectAlignment(long length) { + if ((length & OBJECT_ALIGNMENT_MASK) != 0) { + long padding = OBJECT_ALIGNMENT - (length & OBJECT_ALIGNMENT_MASK); + length += padding; + } + + return length; + } + + private static long sizeOf(Field field) { + if (boolean.class.equals(field.getType())) return 1; + else if (byte.class.equals(field.getType())) return 1; + else if (short.class.equals(field.getType())) return 2; + else if (char.class.equals(field.getType())) return 2; + else if (int.class.equals(field.getType())) return 4; + else if (float.class.equals(field.getType())) return 4; + else return 8; + } + + private static final class Offsets { + public final long min; + public final long max; + + private Offsets(long min, long max) { + this.min = min; + this.max = max; + } + } +}
\ 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 new file mode 100644 index 0000000..d186c77 --- /dev/null +++ b/lang/src/main/java/net/openhft/lang/io/serialization/direct/FieldMetadata.java @@ -0,0 +1,22 @@ +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/direct/Introspect.java b/lang/src/main/java/net/openhft/lang/io/serialization/direct/Introspect.java new file mode 100644 index 0000000..28ff782 --- /dev/null +++ b/lang/src/main/java/net/openhft/lang/io/serialization/direct/Introspect.java @@ -0,0 +1,63 @@ +/* + * 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.io.serialization.direct; + +import net.openhft.lang.Maths; + +import java.lang.reflect.Field; +import java.util.ArrayList; +import java.util.Collections; +import java.util.Comparator; +import java.util.List; + +import static net.openhft.lang.io.NativeBytes.UNSAFE; +import static net.openhft.lang.io.serialization.direct.FieldMetadata.isStatic; + +public class Introspect { + public static List<Field> fields(Class<?> clazz) { + ArrayList<Field> fields = new ArrayList<Field>(); + + addToFields(clazz, fields); + Collections.sort(fields, FieldOffsetComparator.Instance); + + return fields; + } + + private static List<Field> addToFields(Class<?> clazz, ArrayList<Field> accumulator) { + Collections.addAll(accumulator, clazz.getDeclaredFields()); + Class<?> maybeSuper = clazz.getSuperclass(); + + return maybeSuper != null ? + addToFields(maybeSuper, accumulator) : + accumulator; + } + + private static final class FieldOffsetComparator implements Comparator<Field> { + public static final FieldOffsetComparator Instance = new FieldOffsetComparator(); + + @Override + public int compare(Field first, Field second) { + return Maths.compare(offset(first), offset(second)); + } + + private static long offset(Field field) { + return isStatic(field) ? + UNSAFE.staticFieldOffset(field) : + UNSAFE.objectFieldOffset(field); + } + } +}
\ No newline at end of file diff --git a/lang/src/main/java/net/openhft/lang/io/serialization/direct/ObjectMarshaller.java b/lang/src/main/java/net/openhft/lang/io/serialization/direct/ObjectMarshaller.java new file mode 100644 index 0000000..24f8a48 --- /dev/null +++ b/lang/src/main/java/net/openhft/lang/io/serialization/direct/ObjectMarshaller.java @@ -0,0 +1,50 @@ +package net.openhft.lang.io.serialization.direct; + +import net.openhft.lang.io.Bytes; + +import static net.openhft.lang.io.NativeBytes.UNSAFE; +import static net.openhft.lang.io.serialization.direct.DirectSerializationMetadata.SerializationMetadata; + +public final class ObjectMarshaller<T> { + private final SerializationMetadata metadata; + + public ObjectMarshaller(SerializationMetadata metadata) { + this.metadata = metadata; + } + + public void write(Bytes bytes, T tObject) { + long i = metadata.start; + long end = metadata.start + metadata.length; + + while (i < end - 7) { + bytes.writeLong(UNSAFE.getLong(tObject, i)); + i += 8; + } + + while (i < end) { + bytes.writeByte(UNSAFE.getByte(tObject, i)); + ++i; + } + } + + public T read(Bytes bytes, T tObject) { + long i = metadata.start; + long end = metadata.start + metadata.length; + + while (i < end - 7) { + UNSAFE.putLong(tObject, i, bytes.readLong()); + i += 8; + } + + while (i < end) { + UNSAFE.putByte(tObject, i, bytes.readByte()); + ++i; + } + + return tObject; + } + + public long length() { + return metadata.length; + } +}
\ No newline at end of file diff --git a/lang/src/main/java/net/openhft/lang/io/serialization/direct/ObjectMarshallers.java b/lang/src/main/java/net/openhft/lang/io/serialization/direct/ObjectMarshallers.java new file mode 100644 index 0000000..bb9e35d --- /dev/null +++ b/lang/src/main/java/net/openhft/lang/io/serialization/direct/ObjectMarshallers.java @@ -0,0 +1,72 @@ +package net.openhft.lang.io.serialization.direct; + +import net.openhft.lang.io.NativeBytes; + +import java.lang.reflect.Field; +import java.util.*; +import java.util.logging.Logger; + +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()); + + private static final Map<Class, ObjectMarshaller> metadata = new HashMap<Class, ObjectMarshaller>(); + + @SuppressWarnings("unchecked") + public static <T> ObjectMarshaller<T> forClass(Class<T> clazz) { + ObjectMarshaller om = metadata.get(clazz); + if (om == null) { + List<Field> fields = Introspect.fields(clazz); + List<Field> eligibleFields = DirectSerializationFilter.stopAtFirstIneligibleField(fields); + + SerializationMetadata serializationMetadata; + + 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)); + metadata.put(clazz, om); + } + + return (ObjectMarshaller<T>) om; + } + + private static boolean hasIneligibleFields(List<Field> allFields, List<Field> eligibleFields) { + return allFields.size() != eligibleFields.size(); + } + + 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) + )); + } + + private static String commaSeparate(Collection<Field> fields) { + StringBuilder sb = new StringBuilder(); + boolean first = true; + for (Field field : fields) { + if (first) { + sb.append("\t"); + sb.append(field.getName()); + first = false; + } else { + sb.append("\n\t"); + sb.append(field.getName()); + } + } + + return sb.toString(); + } + } +} 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 new file mode 100644 index 0000000..ebcb95f --- /dev/null +++ b/lang/src/main/java/net/openhft/lang/io/serialization/impl/BytesMarshallableMarshaller.java @@ -0,0 +1,52 @@ +/* + * 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.io.serialization.impl; + +import net.openhft.lang.io.Bytes; +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; + +/** + * @author peter.lawrey + */ +public class BytesMarshallableMarshaller<E extends BytesMarshallable> implements BytesMarshaller<E> { + @NotNull + private final Class<E> classMarshaled; + + public BytesMarshallableMarshaller(@NotNull Class<E> classMarshaled) { + this.classMarshaled = classMarshaled; + } + + @Override + public void write(@NotNull Bytes bytes, @NotNull E e) { + e.writeMarshallable(bytes); + } + + @Override + public E read(@NotNull Bytes bytes) { + E e; + try { + e = (E) NativeBytes.UNSAFE.allocateInstance(classMarshaled); + } catch (Exception e2) { + throw new IllegalStateException(e2); + } + e.readMarshallable(bytes); + return e; + } +} 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 new file mode 100644 index 0000000..db36c37 --- /dev/null +++ b/lang/src/main/java/net/openhft/lang/io/serialization/impl/ClassMarshaller.java @@ -0,0 +1,105 @@ +/* + * 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.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 java.lang.ref.WeakReference; +import java.math.BigDecimal; +import java.math.BigInteger; +import java.util.Date; +import java.util.LinkedHashMap; +import java.util.Map; + +/** + * @author peter.lawrey + */ +public class ClassMarshaller 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>(); + + static { + Class[] classes = {Boolean.class, Byte.class, Character.class, Short.class, Integer.class, Long.class, Float.class, Double.class, + String.class, Class.class, BigInteger.class, BigDecimal.class, Date.class}; + for (Class clazz : classes) { + String simpleName = clazz.getSimpleName(); + SC_SHORT_NAME.put(simpleName, clazz); + CS_SHORT_NAME.put(clazz, simpleName); + } + } + + private final ClassLoader classLoader; + private final StringBuilder className = new StringBuilder(40); + @Nullable + @SuppressWarnings("unchecked") + private WeakReference<Class>[] classWeakReference = null; + + public ClassMarshaller(ClassLoader classLoader) { + this.classLoader = classLoader; + } + + @Override + public void write(@NotNull Bytes bytes, @NotNull Class aClass) { + String s = CS_SHORT_NAME.get(aClass); + if (s == null) + s = aClass.getName(); + bytes.writeUTFΔ(s); + } + + @Nullable + @Override + public Class read(@NotNull Bytes bytes) { + className.setLength(0); + bytes.readUTFΔ(className); + return load(className); + } + + @Nullable + private Class load(@NotNull CharSequence name) { + int hash = (int) (Compare.calcLongHashCode(name) & 0x7ffffff) % CACHE_SIZE; + if (classWeakReference == null) + //noinspection unchecked + classWeakReference = new WeakReference[CACHE_SIZE]; + WeakReference<Class> ref = classWeakReference[hash]; + if (ref != null) { + Class clazz = ref.get(); + if (clazz != null && clazz.getName().equals(name)) + return clazz; + } + try { + + Class<?> clazz = SC_SHORT_NAME.get(name.toString()); + if (clazz != null) + return clazz; + clazz = classLoader.loadClass(name.toString()); + classWeakReference[hash] = new WeakReference<Class>(clazz); + return clazz; + } catch (ClassNotFoundException e) { + throw new IllegalArgumentException(e); + } + } + + @Override + public byte code() { + return 'C' & 31; // control C + } +} 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 new file mode 100644 index 0000000..08f66e4 --- /dev/null +++ b/lang/src/main/java/net/openhft/lang/io/serialization/impl/CompactEnumBytesMarshaller.java @@ -0,0 +1,38 @@ +/* + * 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.io.serialization.impl; + +import net.openhft.lang.io.serialization.CompactBytesMarshaller; +import org.jetbrains.annotations.NotNull; + +/** + * Created with IntelliJ IDEA. User: peter Date: 09/12/13 Time: 17:05 To change this template use File | Settings | File + * Templates. + */ +public class CompactEnumBytesMarshaller<E> extends GenericEnumMarshaller<E> implements CompactBytesMarshaller<E> { + private final byte code; + + public CompactEnumBytesMarshaller(@NotNull Class<E> classMarshaled, int capacity, byte code) { + super(classMarshaled, capacity); + this.code = code; + } + + @Override + public byte code() { + return code; + } +} 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 new file mode 100644 index 0000000..03f6bb1 --- /dev/null +++ b/lang/src/main/java/net/openhft/lang/io/serialization/impl/DateMarshaller.java @@ -0,0 +1,95 @@ +/* + * 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.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 java.util.Date; + +/** + * @author peter.lawrey + */ +public class DateMarshaller implements CompactBytesMarshaller<Date> { + final int size1; + private final StringBuilder sb = new StringBuilder(); + @Nullable + private Date[] interner = null; + + public DateMarshaller(int size) { + int size2 = 128; + while (size2 < size && size2 < (1 << 20)) size2 <<= 1; + this.size1 = size2 - 1; + } + + private static long parseLong(@NotNull CharSequence sb) { + long num = 0; + boolean negative = false; + for (int i = 0; i < sb.length(); i++) { + char b = sb.charAt(i); +// if (b >= '0' && b <= '9') + if ((b - ('0' + Integer.MIN_VALUE)) <= 9 + Integer.MIN_VALUE) + num = num * 10 + b - '0'; + else if (b == '-') + negative = true; + else + break; + } + return negative ? -num : num; + } + + @Override + public void write(@NotNull Bytes bytes, @NotNull Date date) { + long pos = bytes.position(); + bytes.writeUnsignedByte(0); + bytes.append(date.getTime()); + bytes.writeUnsignedByte(pos, (int) (bytes.position() - 1 - pos)); + } + + @Nullable + @Override + public Date read(@NotNull Bytes bytes) { + bytes.readUTFΔ(sb); + long time = parseLong(sb); + return lookupDate(time); + } + + @Nullable + private Date lookupDate(long time) { + int idx = hashFor(time); + if (interner == null) + interner = new Date[size1 + 1]; + Date date = interner[idx]; + if (date != null && date.getTime() == time) + return date; + return interner[idx] = new Date(time); + } + + private int hashFor(long time) { + long h = time; + h ^= (h >>> 41) ^ (h >>> 20); + h ^= (h >>> 14) ^ (h >>> 7); + return (int) (h & size1); + } + + @Override + public byte code() { + return 'T' & 31; // 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 new file mode 100644 index 0000000..11f060b --- /dev/null +++ b/lang/src/main/java/net/openhft/lang/io/serialization/impl/EnumBytesMarshaller.java @@ -0,0 +1,93 @@ +/* + * 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.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 java.util.BitSet; +import java.util.LinkedHashMap; +import java.util.Map; + +/** + * @author peter.lawrey + */ +public class EnumBytesMarshaller<E extends Enum<E>> implements BytesMarshaller<E> { + @NotNull + private final Class<E> classMarshaled; + @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 E defaultValue; + private final int mask; + private final StringBuilder reader = new StringBuilder(); + + public EnumBytesMarshaller(@NotNull Class<E> classMarshaled, E defaultValue) { + this.classMarshaled = classMarshaled; + this.defaultValue = defaultValue; + + mask = interner.length - 1; + for (E e : classMarshaled.getEnumConstants()) { + map.put(e.name(), e); + int idx = hashFor(e.name()); + if (!internerDup.get(idx)) { + if (interner[idx] != null) { + //noinspection UnqualifiedFieldAccess,AssignmentToNull + interner[idx] = null; + internerDup.set(idx); + } else { + interner[idx] = e; + } + } + } + } + + @Override + public void write(@NotNull Bytes bytes, @Nullable E e) { + bytes.writeUTFΔ(e == null ? "" : e.name()); + } + + private int hashFor(@NotNull CharSequence cs) { + int h = 0; + + for (int i = 0, length = cs.length(); i < length; i++) + h = 57 * h + cs.charAt(i); + + h ^= (h >>> 20) ^ (h >>> 12); + h ^= (h >>> 7) ^ (h >>> 4); + return h & mask; + } + + @Override + public E read(@NotNull Bytes bytes) { + bytes.readUTFΔ(reader); + return builderToEnum(); + } + + private E builderToEnum() { + int num = hashFor(reader); + int idx = num & mask; + E e = interner[idx]; + if (e != null) return e; + if (!internerDup.get(idx)) return defaultValue; + e = map.get(reader.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 new file mode 100644 index 0000000..05f5390 --- /dev/null +++ b/lang/src/main/java/net/openhft/lang/io/serialization/impl/ExternalizableMarshaller.java @@ -0,0 +1,58 @@ +/* + * 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.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 java.io.Externalizable; +import java.io.IOException; + +/** + * @author peter.lawrey + */ +public class ExternalizableMarshaller<E extends Externalizable> implements BytesMarshaller<E> { + @NotNull + private final Class<E> classMarshaled; + + public ExternalizableMarshaller(@NotNull Class<E> classMarshaled) { + this.classMarshaled = classMarshaled; + } + + @Override + public void write(Bytes bytes, @NotNull E e) { + try { + e.writeExternal(bytes); + } catch (IOException e2) { + throw new IllegalStateException(e2); + } + } + + @Override + public E read(Bytes bytes) { + E e; + try { + e = (E) NativeBytes.UNSAFE.allocateInstance(classMarshaled); + e.readExternal(bytes); + } catch (Exception e2) { + throw new IllegalStateException(e2); + } + return e; + } +} 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 new file mode 100644 index 0000000..8a1b52d --- /dev/null +++ b/lang/src/main/java/net/openhft/lang/io/serialization/impl/GenericEnumMarshaller.java @@ -0,0 +1,93 @@ +/* + * 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.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 java.lang.reflect.Constructor; +import java.lang.reflect.Method; +import java.util.LinkedHashMap; +import java.util.Map; + +/** + * @author peter.lawrey + */ +public class GenericEnumMarshaller<E> implements BytesMarshaller<E> { + @NotNull + private final Class<E> classMarshaled; + @Nullable + private final Constructor<E> constructor; + @Nullable + private final Method valueOf; + @NotNull + private final Map<String, E> map; + + public GenericEnumMarshaller(@NotNull Class<E> classMarshaled, final int capacity) { + this.classMarshaled = classMarshaled; + Constructor<E> constructor = null; + Method valueOf = null; + try { + valueOf = classMarshaled.getMethod("valueOf", String.class); + } catch (NoSuchMethodException e) { + try { + constructor = classMarshaled.getConstructor(String.class); + } catch (NoSuchMethodException e1) { + throw new IllegalArgumentException(classMarshaled + " doesn't have a valueOf(String) or a Constructor(String)"); + } + } + this.constructor = constructor; + this.valueOf = valueOf; + map = new LinkedHashMap<String, E>(128, 0.7f, true) { + @Override + protected boolean removeEldestEntry(Map.Entry<String, E> eldest) { + return size() > capacity; + } + }; + } + + @Override + public void write(@NotNull Bytes bytes, @Nullable E e) { + bytes.writeUTFΔ(e == null ? null : e.toString()); + } + + @Nullable + @Override + public E read(@NotNull Bytes bytes) { + String s = bytes.readUTFΔ(); + return s == null ? null : valueOf(s); + } + + 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()); + } + return e; + } +} 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 new file mode 100644 index 0000000..1369c4a --- /dev/null +++ b/lang/src/main/java/net/openhft/lang/io/serialization/impl/NoMarshaller.java @@ -0,0 +1,38 @@ +/* + * 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.io.serialization.impl; + +import net.openhft.lang.io.Bytes; +import net.openhft.lang.io.serialization.BytesMarshaller; + +/** + * Created with IntelliJ IDEA. User: peter.lawrey Date: 19/09/13 Time: 18:26 To change this template use File | Settings | File + * Templates. + */ +public enum NoMarshaller implements BytesMarshaller<Void> { + INSTANCE; + + @Override + public void write(Bytes bytes, Void aVoid) { + throw new UnsupportedOperationException(); + } + + @Override + public Void read(Bytes bytes) { + throw new UnsupportedOperationException(); + } +} 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 new file mode 100644 index 0000000..2fb2ee5 --- /dev/null +++ b/lang/src/main/java/net/openhft/lang/io/serialization/impl/StringMarshaller.java @@ -0,0 +1,60 @@ +/* + * 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.io.serialization.impl; + +import net.openhft.lang.io.Bytes; +import net.openhft.lang.io.serialization.CompactBytesMarshaller; +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> { + private final int size; + private final StringBuilder reader = new StringBuilder(); + private StringInterner interner; + + public StringMarshaller(int size) { + this.size = size; + } + + @Override + public void write(@NotNull Bytes bytes, String s) { + bytes.writeUTFΔ(s); + } + + @Nullable + @Override + public String read(@NotNull Bytes bytes) { + if (bytes.readUTFΔ(reader)) + return builderToString(); + return null; + } + + + private String builderToString() { + if (interner == null) + interner = new StringInterner(size); + return interner.intern(reader); + } + + public byte code() { + return 'S' & 31; + } +} 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 new file mode 100644 index 0000000..6fc66ee --- /dev/null +++ b/lang/src/main/java/net/openhft/lang/io/serialization/impl/VanillaBytesMarshallerFactory.java @@ -0,0 +1,82 @@ +/* + * 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.io.serialization.impl; + +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 java.io.Externalizable; +import java.util.Date; +import java.util.LinkedHashMap; +import java.util.Map; + +/** + * @author peter.lawrey + */ +public class VanillaBytesMarshallerFactory implements BytesMarshallerFactory { + + private final Map<Class, BytesMarshaller> marshallerMap = new LinkedHashMap<Class, BytesMarshaller>(); + private final BytesMarshaller[] compactMarshallerMap = new BytesMarshaller[256]; + + // private final Map<Class, BytesMarshaller> marshallerTextMap = new LinkedHashMap<Class, BytesMarshaller>(); + { + BytesMarshaller stringMarshaller = new StringMarshaller(16 * 1024); + addMarshaller(String.class, stringMarshaller); + addMarshaller(CharSequence.class, 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))); + } + + @NotNull + @SuppressWarnings("unchecked") + @Override + public <E> BytesMarshaller<E> acquireMarshaller(@NotNull Class<E> eClass, boolean create) { + BytesMarshaller em = marshallerMap.get(eClass); + if (em == null) + if (eClass.isEnum()) + marshallerMap.put(eClass, em = new EnumBytesMarshaller(eClass, null)); + else if (BytesMarshallable.class.isAssignableFrom(eClass)) + marshallerMap.put(eClass, em = new BytesMarshallableMarshaller((Class) eClass)); + else if (Externalizable.class.isAssignableFrom(eClass)) + marshallerMap.put(eClass, em = new ExternalizableMarshaller((Class) eClass)); + else { + try { + marshallerMap.put(eClass, em = new GenericEnumMarshaller<E>(eClass, 1000)); + } catch (Exception e) { + marshallerMap.put(eClass, em = NoMarshaller.INSTANCE); + } + } + return em; + } + + @Override + public <E> BytesMarshaller<E> getMarshaller(byte code) { + return compactMarshallerMap[code & 0xFF]; + } + + public <E> void addMarshaller(Class<E> eClass, BytesMarshaller<E> marshaller) { + marshallerMap.put(eClass, marshaller); + if (marshaller instanceof CompactBytesMarshaller) + compactMarshallerMap[((CompactBytesMarshaller) marshaller).code()] = marshaller; + } +} diff --git a/lang/src/main/java/net/openhft/lang/model/Byteable.java b/lang/src/main/java/net/openhft/lang/model/Byteable.java new file mode 100644 index 0000000..a6c1a54 --- /dev/null +++ b/lang/src/main/java/net/openhft/lang/model/Byteable.java @@ -0,0 +1,34 @@ +/* + * 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.model; + +import net.openhft.lang.io.Bytes; + +/** + * User: peter.lawrey + * Date: 07/10/13 + * Time: 21:38 + */ +public interface Byteable { + void bytes(Bytes bytes); + + void bytes(Bytes bytes, long offset); + + Bytes bytes(); + + int maxSize(); +}
\ No newline at end of file diff --git a/lang/src/main/java/net/openhft/lang/model/ClassModel.java b/lang/src/main/java/net/openhft/lang/model/ClassModel.java new file mode 100644 index 0000000..40bc614 --- /dev/null +++ b/lang/src/main/java/net/openhft/lang/model/ClassModel.java @@ -0,0 +1,29 @@ +/* + * 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.model; + +import java.lang.reflect.Method; +import java.util.Map; +import java.util.Set; + +public interface ClassModel { + void setMethod(String name, MethodFilter filter, Method method); + + Set<String> getNames(); + + Map<MethodFilter, Method> filterMapFor(String name); +} diff --git a/lang/src/main/java/net/openhft/lang/model/CodeGenerator.java b/lang/src/main/java/net/openhft/lang/model/CodeGenerator.java new file mode 100644 index 0000000..d1df93c --- /dev/null +++ b/lang/src/main/java/net/openhft/lang/model/CodeGenerator.java @@ -0,0 +1,20 @@ +/* + * 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.model; + +public 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 new file mode 100644 index 0000000..79baf90 --- /dev/null +++ b/lang/src/main/java/net/openhft/lang/model/Copyable.java @@ -0,0 +1,31 @@ +/* + * 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.model; + +/** + * User: peter.lawrey + * Date: 08/10/13 + * Time: 07:45 + */ +public interface Copyable<T> { + /** + * Copy from this type. + * + * @param from to copy from + */ + void copyFrom(T from); +} diff --git a/lang/src/main/java/net/openhft/lang/model/DataValueGenerator.java b/lang/src/main/java/net/openhft/lang/model/DataValueGenerator.java new file mode 100644 index 0000000..97ff8fe --- /dev/null +++ b/lang/src/main/java/net/openhft/lang/model/DataValueGenerator.java @@ -0,0 +1,483 @@ +/* + * 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.model; + +import net.openhft.compiler.CachedCompiler; +import net.openhft.lang.Compare; +import net.openhft.lang.Maths; +import net.openhft.lang.io.Bytes; +import net.openhft.lang.io.serialization.BytesMarshallable; + +import java.io.IOException; +import java.io.ObjectInput; +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 + */ +public class DataValueGenerator { + public 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>>() { + @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; + } + }; + 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 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) { + try { + return (T) acquireHeapClass(tClass).newInstance(); + } catch (Exception e) { + throw new AssertionError(e); + } + } + + 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 String generateHeapObject(DataValueModel<?> dvmodel) { + SortedSet<Class> imported = new TreeSet<Class>(COMPARATOR); + imported.add(BytesMarshallable.class); + imported.add(Bytes.class); + imported.add(IOException.class); + imported.add(Copyable.class); + imported.add(dvmodel.type()); + + StringBuilder fieldDeclarations = new StringBuilder(); + StringBuilder getterSetters = new StringBuilder(); + 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); + 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")) + imported.add(type); + fieldDeclarations.append(" private ").append(type.getName()).append(" _").append(name).append(";\n"); + + 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"); + } 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"); + } + getterSetters.append(" public ").append(type.getName()).append(' ').append(getter.getName()).append("() {\n"); + getterSetters.append(" return _").append(name).append(";\n"); + getterSetters.append(" }\n\n"); + 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") + .append(" }"); + } + 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(" }"); + } + 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(" if (_").append(name).append(" == _1) {\n") + .append(" _").append(name).append(" = _2;\n") + .append(" return true;\n") + .append(" }\n") + .append(" return false;\n") + .append(" }\n"); + } + Method tryLockNanos = model.tryLockNanos(); + if (tryLockNanos != null) { + getterSetters.append(" public boolean ").append(tryLockNanos.getName()).append("(long nanos) {\n") + .append(" throw new UnsupportedOperationException();\n") + .append(" }"); + } + Method tryLock = model.tryLock(); + if (tryLock != null) { + getterSetters.append(" public boolean ").append(tryLock.getName()).append("() {\n") + .append(" throw new UnsupportedOperationException();\n") + .append(" }"); + } + Method unlock = model.unlock(); + if (unlock != null) { + getterSetters.append(" public void ").append(unlock.getName()).append("() {\n") + .append(" throw new UnsupportedOperationException();\n") + .append(" }"); + } + Method busyLock = model.busyLock(); + if (busyLock != null) { + getterSetters.append(" public void ").append(busyLock.getName()).append("() {\n") + .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"); + } + StringBuilder sb = new StringBuilder(); + sb.append("package ").append(dvmodel.type().getPackage().getName()).append(";\n\n"); + 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("£heap implements ").append(dvmodel.type().getSimpleName()) + .append(", BytesMarshallable, Copyable<").append(dvmodel.type().getName()).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(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"); + 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 int maxSize() {\n"); + sb.append(" throw new UnsupportedOperationException();\n"); + sb.append(" }\n"); + } + generateObjectMethods(sb, dvmodel, entries); + sb.append("}\n"); +// System.out.println(sb); + return sb.toString(); + } + + private static void generateObjectMethods(StringBuilder sb, DataValueModel<?> dvmodel, Map.Entry<String, FieldModel>[] entries) { + int count = 0; + StringBuilder hashCode = new StringBuilder(); + StringBuilder equals = 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++; + } + sb.append(" public int hashCode() {\n" + + " long lhc = longHashCode();\n" + + " return (int) ((lhc >>> 32) ^ lhc);\n" + + " }\n" + + "\n" + + " public long longHashCode() {\n" + + " return "); + for (int i = 1; i < count; i++) + sb.append('('); + sb.append(hashCode); + String simpleName = dvmodel.type().getSimpleName(); + sb.append(";\n") + .append(" }\n") + .append("\n") + .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(" }"); + + + } + + public <T> T nativeInstance(Class<T> tClass) { + try { + return (T) acquireNativeClass(tClass).newInstance(); + } catch (Exception e) { + throw new AssertionError(e); + } + } + + public <T> Class acquireNativeClass(Class<T> tClass) throws ClassNotFoundException { + Class nativeClass = nativeClassMap.get(tClass); + if (nativeClass != null) + return nativeClass; + DataValueModel<T> dvmodel = DataValueModels.acquireModel(tClass); + for (Class clazz : dvmodel.nestedModels()) { + Class clazz2 = acquireNativeClass(clazz); + } + String actual = new DataValueGenerator().generateNativeObject(dvmodel); + if (dumpCode) + LOGGER.info(actual); + nativeClass = cc.loadFromJava(tClass.getClassLoader(), tClass.getName() + "£native", actual); + nativeClassMap.put(tClass, nativeClass); + return nativeClass; + } + + public String generateNativeObject(Class<?> tClass) { + return generateNativeObject(DataValueModels.acquireModel(tClass)); + } + + public String generateNativeObject(DataValueModel<?> dvmodel) { + SortedSet<Class> imported = new TreeSet<Class>(COMPARATOR); + imported.add(BytesMarshallable.class); + imported.add(ObjectOutput.class); + imported.add(ObjectInput.class); + imported.add(IOException.class); + imported.add(Copyable.class); + imported.add(Byteable.class); + imported.add(Bytes.class); + + StringBuilder staticFieldDeclarations = new StringBuilder(); + StringBuilder fieldDeclarations = new StringBuilder(); + StringBuilder getterSetters = new StringBuilder(); + StringBuilder writeMarshal = new StringBuilder(); + 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); + 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")) + imported.add(type); + String NAME = "_offset + " + name.toUpperCase(); + final Method setter = model.setter(); + final Method getter = model.getter(); + 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"); + 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") + .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") + .append(" }"); + } + 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(" return _bytes.compareAndSwap").append(bytesType(type)).append('(').append(NAME).append(", _1, _2);\n") + .append(" }"); + } + Method tryLockNanos = model.tryLockNanos(); + if (tryLockNanos != null) { + getterSetters.append(" public boolean ").append(tryLockNanos.getName()).append("(long nanos) {\n") + .append(" return _bytes.tryLockNanos").append(bytesType(type)).append('(').append(NAME).append(", nanos);\n") + .append(" }"); + } + Method tryLock = model.tryLock(); + if (tryLock != null) { + getterSetters.append(" public boolean ").append(tryLock.getName()).append("() {\n") + .append(" return _bytes.tryLock").append(bytesType(type)).append('(').append(NAME).append(");\n") + .append(" }"); + } + Method unlock = model.unlock(); + if (unlock != null) { + getterSetters.append(" public void ").append(unlock.getName()).append("() {\n") + .append(" _bytes.unlock").append(bytesType(type)).append('(').append(NAME).append(");\n") + .append(" }"); + } + Method busyLock = model.busyLock(); + if (busyLock != null) { + getterSetters.append(" public void ").append(busyLock.getName()).append("() throws InterruptedException {\n") + .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; + } 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) { + 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"); + } + + 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; + } + } + } + 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"); + 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"); + 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("}\n"); +// System.out.println(sb); + return sb.toString(); + } + + public boolean isDumpCode() { + return dumpCode; + } + + public void setDumpCode(boolean dumpCode) { + this.dumpCode = dumpCode; + } +} diff --git a/lang/src/main/java/net/openhft/lang/model/DataValueMetaModel.java b/lang/src/main/java/net/openhft/lang/model/DataValueMetaModel.java new file mode 100644 index 0000000..75263f5 --- /dev/null +++ b/lang/src/main/java/net/openhft/lang/model/DataValueMetaModel.java @@ -0,0 +1,60 @@ +/* + * 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.model; + +import net.openhft.lang.io.serialization.BytesMarshallable; + +import java.io.Externalizable; +import java.util.ArrayList; +import java.util.HashSet; +import java.util.List; +import java.util.Set; + +public class DataValueMetaModel { + private final Set<Class> ignoredClasses = new HashSet<Class>(); + private final List<MethodFilter> filters = new ArrayList<MethodFilter>(); + + public DataValueMetaModel() { + addIgnoredClass(Object.class); + addIgnoredClass(Externalizable.class); + addIgnoredClass(Copyable.class); + addIgnoredClass(Byteable.class); + addIgnoredClass(BytesMarshallable.class); + + for (VanillaFilter vanillaFilter : VanillaFilter.values()) { + addMethodFilter(vanillaFilter); + } + } + + public void addIgnoredClass(Class aClass) { + ignoredClasses.add(aClass); + } + + public void addMethodFilter(MethodFilter filter) { + int pos = insertionPoint(filter); + filters.add(pos, filter); + } + + private int insertionPoint(MethodFilter filter) { + for (int i = 0; i < filters.size(); i++) + if (filters.get(i).matches() < filter.matches()) + return i; + return filters.size(); + } + + +} diff --git a/lang/src/main/java/net/openhft/lang/model/DataValueModel.java b/lang/src/main/java/net/openhft/lang/model/DataValueModel.java new file mode 100644 index 0000000..23e1567 --- /dev/null +++ b/lang/src/main/java/net/openhft/lang/model/DataValueModel.java @@ -0,0 +1,37 @@ +/* + * 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.model; + +import java.util.Map; +import java.util.Set; + +/** + * User: peter.lawrey + * Date: 06/10/13 + * Time: 17:06 + */ +public interface DataValueModel<T> { + Map<String, ? extends FieldModel> fieldMap(); + + boolean isScalar(Class<?> nClass); + + Set<Class> nestedModels(); + + <N> DataValueModel<N> nestedModel(Class<N> nClass); + + Class<T> type(); +}
\ No newline at end of file diff --git a/lang/src/main/java/net/openhft/lang/model/DataValueModelImpl.java b/lang/src/main/java/net/openhft/lang/model/DataValueModelImpl.java new file mode 100644 index 0000000..bc5b03b --- /dev/null +++ b/lang/src/main/java/net/openhft/lang/model/DataValueModelImpl.java @@ -0,0 +1,461 @@ +/* + * 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.model; + +import net.openhft.lang.io.serialization.BytesMarshallable; +import net.openhft.lang.model.constraints.Digits; +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; + +/** + * User: peter.lawrey + * Date: 06/10/13 + * Time: 17:23 + */ +public class DataValueModelImpl<T> implements DataValueModel<T> { + static final Map<Class, Integer> HEAP_SIZE_MAP = new HashMap<Class, Integer>(); + + static { + HEAP_SIZE_MAP.put(boolean.class, 1); + HEAP_SIZE_MAP.put(byte.class, 8); + HEAP_SIZE_MAP.put(char.class, 16); + HEAP_SIZE_MAP.put(short.class, 16); + HEAP_SIZE_MAP.put(int.class, 32); + HEAP_SIZE_MAP.put(float.class, 32); + HEAP_SIZE_MAP.put(long.class, 64); + HEAP_SIZE_MAP.put(double.class, 64); + } + + private final Map<String, FieldModelImpl> fieldModelMap = new TreeMap<String, FieldModelImpl>(); + private final Class<T> type; + private final Map<Class, DataValueModel> nestedMap = new HashMap<Class, DataValueModel>(); + + public DataValueModelImpl(Class<T> type) { + this.type = type; + if (!type.isInterface()) + throw new IllegalArgumentException("type must be an interface, was " + type); + + Method[] methods = type.getMethods(); + for (Method method : methods) { + Class<?> declaringClass = method.getDeclaringClass(); + if (declaringClass == Object.class + || declaringClass == Externalizable.class + || declaringClass == BytesMarshallable.class + || declaringClass == Copyable.class + || declaringClass == Byteable.class) + continue; + String name = method.getName(); + Class<?>[] parameterTypes = method.getParameterTypes(); + final Class<?> returnType = method.getReturnType(); + switch (parameterTypes.length) { + case 0: { + String name5 = getUnlock(name); + if (name5 != null && returnType == void.class) { + FieldModelImpl fm = acquireField(name5); + fm.unlock(method); + break; + } + String name4 = getBusyLock(name); + if (name4 != null && returnType == void.class) { + FieldModelImpl fm = acquireField(name4); + fm.busyLock(method); + break; + } + String name3 = getTryLock(name); + if (name3 != null && returnType == boolean.class) { + FieldModelImpl fm = acquireField(name3); + fm.tryLock(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); + break; + } + case 1: { + String name5 = getTryLockNanos(name); + if (name5 != null && returnType == boolean.class) { + FieldModelImpl fm = acquireField(name5); + fm.tryLockNanos(method); + break; + } + + String name4 = getAtomicAdder(name); + if (name4 != null) { + FieldModelImpl fm = acquireField(name4); + fm.atomicAdder(method); + break; + } + + String name3 = getAdder(name); + if (name3 != null) { + FieldModelImpl fm = acquireField(name3); + fm.adder(method); + break; + } + + String name6 = getGetter(name, returnType); + if (name6 != null && parameterTypes[0] == int.class && returnType != void.class) { + 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); + break; + } + case 2: { + String name2 = getCAS(name); + if (name2 != null && returnType == boolean.class) { + FieldModelImpl fm = acquireField(name2); + fm.cas(method); + break; + } + String name3 = getSetter(name); + if (name3 != null && parameterTypes[0] == int.class && returnType == void.class) { + 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."); + Class ftype = model.type(); + if (!isScalar(ftype) && !nestedMap.containsKey(ftype)) + nestedMap.put(ftype, new DataValueModelImpl(ftype)); + } + } + + private static String getCAS(String name) { + final int len = 14; + if (name.length() > len && name.startsWith("compareAndSwap") && 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))) + return Character.toLowerCase(name.charAt(len)) + name.substring(len + 1); + return null; + } + + private static String getAdder(String name) { + final int len = 3; + if (name.length() > len && name.startsWith("add") && Character.isUpperCase(name.charAt(len))) + return Character.toLowerCase(name.charAt(len)) + name.substring(len + 1); + return null; + } + + private static String getSetter(String name) { + final int len = 3; + if (name.length() > len && name.startsWith("set") && Character.isUpperCase(name.charAt(len))) + return Character.toLowerCase(name.charAt(len)) + name.substring(len + 1); + 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); + if ((returnType == boolean.class || returnType == Boolean.class) + && name.length() > 2 && name.startsWith("is") && Character.isUpperCase(name.charAt(2))) + return Character.toLowerCase(name.charAt(2)) + name.substring(3); + return name; + } + + private 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) { + 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) { + 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) { + 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; + } + + private FieldModelImpl acquireField(String name) { + FieldModelImpl fieldModelImpl = fieldModelMap.get(name); + if (fieldModelImpl == null) + fieldModelMap.put(name, fieldModelImpl = new FieldModelImpl(name)); + + return fieldModelImpl; + } + + @Override + public Map<String, ? extends FieldModel> fieldMap() { + return fieldModelMap; + } + + public boolean isScalar(Class type) { + return type.isPrimitive() || CharSequence.class.isAssignableFrom(type); + } + + @Override + public Set<Class> nestedModels() { + return nestedMap.keySet(); + } + + @Override + public <N> DataValueModel<N> nestedModel(Class<N> nClass) { + @SuppressWarnings("unchecked") + DataValueModel<N> model = (DataValueModel<N>) (nClass == type ? this : nestedMap.get(nClass)); + return model; + } + + @Override + public Class<T> type() { + return type; + } + + static class FieldModelImpl<T> implements FieldModel<T> { + + private final String name; + private Method getter, setter; + private Digits digits; + private Range range; + private MaxSize maxSize; + private MaxSize indexSize; + private Method adder; + private Method atomicAdder; + private Method cas; + private Method tryLockNanos; + private Method tryLock; + private Method busyLock; + private Method unlock; + private Method indexedGetter; + private Method indexedSetter; + + public FieldModelImpl(String name) { + this.name = name; + } + + public String name() { + return name; + } + + public void getter(Method getter) { + this.getter = getter; + } + + public Method getter() { + return getter; + } + + public void setter(Method setter) { + this.setter = setter; + for (Annotation a : setter.getParameterAnnotations()[0]) { + if (a instanceof Digits) + digits = (Digits) a; + if (a instanceof Range) + range = (Range) a; + if (a instanceof MaxSize) + maxSize = (MaxSize) a; + } + } + + public Method setter() { + return setter; + } + + @Override + public Class<T> type() { + return (Class<T>) (getter != null ? getter.getReturnType() : + indexedGetter != null ? indexedGetter.getReturnType() : null); + } + + public void adder(Method method) { + adder = method; + } + + public Method adder() { + return adder; + } + + @Override + public int heapSize() { + Integer size = HEAP_SIZE_MAP.get(type()); + if (size == null) return -1; + return size; + } + + // maxSize in bits. + @Override + public int nativeSize() { + 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; + } + + @Override + public Digits digits() { + return digits; + } + + @Override + public Range range() { + return range; + } + + @Override + public MaxSize size() { + return maxSize; + } + + @Override + public String toString() { + return "FieldModel{" + + "name='" + name + '\'' + + ", getter=" + (indexedGetter != null ? indexedGetter : getter) + + ", setter=" + (indexedSetter != null ? indexedSetter : setter) + + (digits == null ? "" : ", digits= " + digits) + + (range == null ? "" : ", range= " + range) + + (maxSize == null ? "" : ", size= " + maxSize) + + ((indexedGetter == null && indexedSetter == null) ? "" : ", indexSize= " + indexSize) + + '}'; + } + + public void atomicAdder(Method method) { + atomicAdder = method; + } + + public Method atomicAdder() { + return atomicAdder; + } + + public void cas(Method method) { + cas = method; + } + + public Method cas() { + return cas; + } + + public void tryLockNanos(Method method) { + tryLockNanos = method; + } + + public Method tryLockNanos() { + return tryLockNanos; + } + + public void tryLock(Method tryLock) { + this.tryLock = tryLock; + } + + public Method tryLock() { + return tryLock; + } + + public void busyLock(Method busyLock) { + this.busyLock = busyLock; + } + + public Method busyLock() { + return busyLock; + } + + public void unlock(Method unlock) { + this.unlock = unlock; + } + + public Method unlock() { + return unlock; + } + + public void indexSize(MaxSize indexSize) { + if (indexSize != null) + this.indexSize = indexSize; + } + + public MaxSize indexSize() { + return indexSize; + } + + public void indexedGetter(Method indexedGetter) { + this.indexedGetter = indexedGetter; + indexAnnotations(indexedGetter); + } + + public Method indexedGetter() { + return indexedGetter; + } + + public void indexedSetter(Method indexedSetter) { + this.indexedSetter = indexedSetter; + indexAnnotations(indexedSetter); + } + + public void indexAnnotations(Method method) { + for (Annotation a : method.getParameterAnnotations()[0]) { +// if (a instanceof Digits) +// digits = (Digits) a; +// if (a instanceof Range) +// range = (Range) a; + if (a instanceof MaxSize) + indexSize = (MaxSize) a; + } + } + + public Method indexedSetter() { + return indexedSetter; + } + } +} diff --git a/lang/src/main/java/net/openhft/lang/model/DataValueModels.java b/lang/src/main/java/net/openhft/lang/model/DataValueModels.java new file mode 100644 index 0000000..bda9620 --- /dev/null +++ b/lang/src/main/java/net/openhft/lang/model/DataValueModels.java @@ -0,0 +1,47 @@ +/* + * 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.model; + +import java.util.Map; +import java.util.WeakHashMap; + +/** + * User: peter.lawrey + * Date: 06/10/13 + * Time: 17:14 + */ +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) { + return MODEL_MAP.get(tClass); + } + + public 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) { + DataValueModel<T> model = getModel(tClass); + 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 new file mode 100644 index 0000000..33869d0 --- /dev/null +++ b/lang/src/main/java/net/openhft/lang/model/FieldModel.java @@ -0,0 +1,62 @@ +/* + * 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.model; + +import net.openhft.lang.model.constraints.Digits; +import net.openhft.lang.model.constraints.MaxSize; +import net.openhft.lang.model.constraints.Range; + +import java.lang.reflect.Method; + +/** + * User: peter.lawrey + * Date: 06/10/13 + * Time: 18:22 + */ +public interface FieldModel<T> { + String name(); + + Method getter(); + + Method setter(); + + Method adder(); + + Method atomicAdder(); + + Method cas(); + + Method tryLockNanos(); + + Method tryLock(); + + Method busyLock(); + + Method unlock(); + + Class<T> type(); + + int heapSize(); + + int nativeSize(); + + Digits digits(); + + Range range(); + + MaxSize size(); +} diff --git a/lang/src/main/java/net/openhft/lang/model/HeapCodeGenerator.java b/lang/src/main/java/net/openhft/lang/model/HeapCodeGenerator.java new file mode 100644 index 0000000..78886a4 --- /dev/null +++ b/lang/src/main/java/net/openhft/lang/model/HeapCodeGenerator.java @@ -0,0 +1,161 @@ +/* + * 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.model; + +public enum HeapCodeGenerator implements CodeGenerator { + /* GET_VALUE { + public void addCode(CodeModel codeModel, Method method) { + StringBuilder sb = codeModel.acquireMethod() + } + }, + SET_VALUE { + @Override + public int matches() { + return 3; + } + + @Override + public String nameFor(Method method, Class<?>[] parameterTypes) { + if (parameterTypes.length != 1 || method.getReturnType() == void.class) + return null; + final String name = method.getName(); + final int pos = matches(); + if (name.startsWith("set") && Character.isUpperCase(name.charAt(pos))) + return Character.toLowerCase(name.charAt(pos)) + name.substring(pos + 1); + return null; + } + }, + ADD_VALUE { + @Override + public int matches() { + return 3; + } + + @Override + public String nameFor(Method method, Class<?>[] parameterTypes) { + if (parameterTypes.length != 1) + return null; + final String name = method.getName(); + final int pos = matches(); + if (name.startsWith("add") && Character.isUpperCase(name.charAt(pos))) + return Character.toLowerCase(name.charAt(pos)) + name.substring(pos + 1); + return null; + } + }, + ADD_ATOMIC_VALUE { + @Override + public int matches() { + return 8; + } + + @Override + public String nameFor(Method method, Class<?>[] parameterTypes) { + if (parameterTypes.length != 1) + return null; + final String name = method.getName(); + final int pos = matches(); + if (name.startsWith("addAtomic") && Character.isUpperCase(name.charAt(pos))) + return Character.toLowerCase(name.charAt(pos)) + name.substring(pos + 1); + return null; + } + }, + COMPARE_AND_SWAP_VALUE { + @Override + public int matches() { + return 15; + } + + @Override + public String nameFor(Method method, Class<?>[] parameterTypes) { + if (parameterTypes.length != 1) + return null; + final String name = method.getName(); + final int pos = matches(); + if (name.startsWith("compareAndSwap") && Character.isUpperCase(name.charAt(pos))) + return Character.toLowerCase(name.charAt(pos)) + name.substring(pos + 1); + return null; + } + }, + TRY_LOCK_VALUE { + @Override + public int matches() { + return 7; + } + + @Override + public String nameFor(Method method, Class<?>[] parameterTypes) { + if (parameterTypes.length != 1) + return null; + final String name = method.getName(); + final int pos = matches(); + if (name.startsWith("tryLock") && Character.isUpperCase(name.charAt(pos))) + return Character.toLowerCase(name.charAt(pos)) + name.substring(pos + 1); + return null; + } + }, + TRY_LOCK_NANOS_VALUE { + @Override + public int matches() { + return 12; + } + + @Override + public String nameFor(Method method, Class<?>[] parameterTypes) { + if (parameterTypes.length != 1) + return null; + final String name = method.getName(); + final int pos = matches(); + if (name.startsWith("tryLockNanos") && Character.isUpperCase(name.charAt(pos))) + return Character.toLowerCase(name.charAt(pos)) + name.substring(pos + 1); + return null; + } + }, + BUSY_LOCK_VALUE { + @Override + public int matches() { + return 8; + } + + @Override + public String nameFor(Method method, Class<?>[] parameterTypes) { + if (parameterTypes.length != 1) + return null; + final String name = method.getName(); + final int pos = matches(); + if (name.startsWith("busyLock") && Character.isUpperCase(name.charAt(pos))) + return Character.toLowerCase(name.charAt(pos)) + name.substring(pos + 1); + return null; + } + }, + UNLOCK_VALUE { + @Override + public int matches() { + return 6; + } + + @Override + public String nameFor(Method method, Class<?>[] parameterTypes) { + if (parameterTypes.length != 1) + return null; + final String name = method.getName(); + final int pos = matches(); + if (name.startsWith("unlock") && Character.isUpperCase(name.charAt(pos))) + return Character.toLowerCase(name.charAt(pos)) + name.substring(pos + 1); + return null; + } + },*/ +} diff --git a/lang/src/main/java/net/openhft/lang/model/MethodFilter.java b/lang/src/main/java/net/openhft/lang/model/MethodFilter.java new file mode 100644 index 0000000..32a3908 --- /dev/null +++ b/lang/src/main/java/net/openhft/lang/model/MethodFilter.java @@ -0,0 +1,25 @@ +/* + * 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.model; + +import java.lang.reflect.Method; + +public 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/src/main/java/net/openhft/lang/model/MethodTemplate.java new file mode 100644 index 0000000..1a38eee --- /dev/null +++ b/lang/src/main/java/net/openhft/lang/model/MethodTemplate.java @@ -0,0 +1,23 @@ +/* + * 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.model; + +import java.lang.reflect.Method; + +public interface MethodTemplate { + void generate(Method method, ClassModel classModel); +} diff --git a/lang/src/main/java/net/openhft/lang/model/VanillaFilter.java b/lang/src/main/java/net/openhft/lang/model/VanillaFilter.java new file mode 100644 index 0000000..3ec00bc --- /dev/null +++ b/lang/src/main/java/net/openhft/lang/model/VanillaFilter.java @@ -0,0 +1,175 @@ +/* + * 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.model; + +import java.lang.reflect.Method; + +public enum VanillaFilter implements MethodFilter { + GET_VALUE { + @Override + public int matches() { + return 3; + } + + @Override + public String nameFor(Method method, Class<?>[] parameterTypes) { + if (parameterTypes.length != 0 || method.getReturnType() != void.class) + return null; + final String name = method.getName(); + final int pos = matches(); + if (name.startsWith("get") && Character.isUpperCase(name.charAt(pos))) + return Character.toLowerCase(name.charAt(pos)) + name.substring(pos + 1); + return null; + } + }, + SET_VALUE { + @Override + public int matches() { + return 3; + } + + @Override + public String nameFor(Method method, Class<?>[] parameterTypes) { + if (parameterTypes.length != 1 || method.getReturnType() == void.class) + return null; + final String name = method.getName(); + final int pos = matches(); + if (name.startsWith("set") && Character.isUpperCase(name.charAt(pos))) + return Character.toLowerCase(name.charAt(pos)) + name.substring(pos + 1); + return null; + } + }, + ADD_VALUE { + @Override + public int matches() { + return 3; + } + + @Override + public String nameFor(Method method, Class<?>[] parameterTypes) { + if (parameterTypes.length != 1) + return null; + final String name = method.getName(); + final int pos = matches(); + if (name.startsWith("add") && Character.isUpperCase(name.charAt(pos))) + return Character.toLowerCase(name.charAt(pos)) + name.substring(pos + 1); + return null; + } + }, + ADD_ATOMIC_VALUE { + @Override + public int matches() { + return 8; + } + + @Override + public String nameFor(Method method, Class<?>[] parameterTypes) { + if (parameterTypes.length != 1) + return null; + final String name = method.getName(); + final int pos = matches(); + if (name.startsWith("addAtomic") && Character.isUpperCase(name.charAt(pos))) + return Character.toLowerCase(name.charAt(pos)) + name.substring(pos + 1); + return null; + } + }, + COMPARE_AND_SWAP_VALUE { + @Override + public int matches() { + return 15; + } + + @Override + public String nameFor(Method method, Class<?>[] parameterTypes) { + if (parameterTypes.length != 1) + return null; + final String name = method.getName(); + final int pos = matches(); + if (name.startsWith("compareAndSwap") && Character.isUpperCase(name.charAt(pos))) + return Character.toLowerCase(name.charAt(pos)) + name.substring(pos + 1); + return null; + } + }, + TRY_LOCK_VALUE { + @Override + public int matches() { + return 7; + } + + @Override + public String nameFor(Method method, Class<?>[] parameterTypes) { + if (parameterTypes.length != 1) + return null; + final String name = method.getName(); + final int pos = matches(); + if (name.startsWith("tryLock") && Character.isUpperCase(name.charAt(pos))) + return Character.toLowerCase(name.charAt(pos)) + name.substring(pos + 1); + return null; + } + }, + TRY_LOCK_NANOS_VALUE { + @Override + public int matches() { + return 12; + } + + @Override + public String nameFor(Method method, Class<?>[] parameterTypes) { + if (parameterTypes.length != 1) + return null; + final String name = method.getName(); + final int pos = matches(); + if (name.startsWith("tryLockNanos") && Character.isUpperCase(name.charAt(pos))) + return Character.toLowerCase(name.charAt(pos)) + name.substring(pos + 1); + return null; + } + }, + BUSY_LOCK_VALUE { + @Override + public int matches() { + return 8; + } + + @Override + public String nameFor(Method method, Class<?>[] parameterTypes) { + if (parameterTypes.length != 1) + return null; + final String name = method.getName(); + final int pos = matches(); + if (name.startsWith("busyLock") && Character.isUpperCase(name.charAt(pos))) + return Character.toLowerCase(name.charAt(pos)) + name.substring(pos + 1); + return null; + } + }, + UNLOCK_VALUE { + @Override + public int matches() { + return 6; + } + + @Override + public String nameFor(Method method, Class<?>[] parameterTypes) { + if (parameterTypes.length != 1) + return null; + final String name = method.getName(); + final int pos = matches(); + if (name.startsWith("unlock") && Character.isUpperCase(name.charAt(pos))) + return Character.toLowerCase(name.charAt(pos)) + name.substring(pos + 1); + return null; + } + }, +} 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 new file mode 100644 index 0000000..4a1b1ee --- /dev/null +++ b/lang/src/main/java/net/openhft/lang/model/constraints/Digits.java @@ -0,0 +1,36 @@ +/* + * 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.model.constraints; + +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.RUNTIME; + +/** + * The maximum size <b>in encoded bytes</b> for a variable length data type. + */ +@Target(PARAMETER) +@Retention(RUNTIME) +@Documented +public @interface Digits { + int integer() default 12; + + int decimal() default 6; +} 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 new file mode 100644 index 0000000..4ca6c4a --- /dev/null +++ b/lang/src/main/java/net/openhft/lang/model/constraints/MaxSize.java @@ -0,0 +1,34 @@ +/* + * 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.model.constraints; + +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.RUNTIME; + +/** + * The maximum size <b>in encoded bytes</b> for a variable length data type. The units is either elements, or bytes. + */ +@Target(PARAMETER) +@Retention(RUNTIME) +@Documented +public @interface MaxSize { + int value() default 64; +} 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 new file mode 100644 index 0000000..2e99420 --- /dev/null +++ b/lang/src/main/java/net/openhft/lang/model/constraints/Range.java @@ -0,0 +1,36 @@ +/* + * 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.model.constraints; + +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.RUNTIME; + +/** + * The lowest to highest values allowed (inclusive) + */ +@Target(PARAMETER) +@Retention(RUNTIME) +@Documented +public @interface Range { + long min() default Long.MIN_VALUE; + + long max() default Long.MAX_VALUE; +} diff --git a/lang/src/main/java/net/openhft/lang/pool/StringInterner.java b/lang/src/main/java/net/openhft/lang/pool/StringInterner.java new file mode 100755 index 0000000..3796bc0 --- /dev/null +++ b/lang/src/main/java/net/openhft/lang/pool/StringInterner.java @@ -0,0 +1,78 @@ +/* + * 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.pool; + +import net.openhft.lang.Maths; +import org.jetbrains.annotations.NotNull; +import org.jetbrains.annotations.Nullable; + +/** + * @author peter.lawrey + */ +public class StringInterner { + @NotNull + private final String[] interner; + private final int mask; + + public StringInterner(int capacity) { + int n = Maths.nextPower2(capacity, 128); + interner = new String[n]; + mask = n - 1; + } + + private 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; + } + +/* 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; + }*/ + + @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; + String s = interner[h]; + if (isEqual(s, cs)) + return s; + String s2 = cs.toString(); + return interner[h] = s2; + } +} diff --git a/lang/src/main/java/net/openhft/lang/testing/Differencer.java b/lang/src/main/java/net/openhft/lang/testing/Differencer.java new file mode 100644 index 0000000..29d44f1 --- /dev/null +++ b/lang/src/main/java/net/openhft/lang/testing/Differencer.java @@ -0,0 +1,26 @@ +/* + * 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.testing; + +/** + * User: peter.lawrey + * Date: 05/08/13 + * Time: 19:06 + */ +public 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 new file mode 100644 index 0000000..983f114 --- /dev/null +++ b/lang/src/main/java/net/openhft/lang/testing/RunningMinimum.java @@ -0,0 +1,55 @@ +/* + * 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.testing; + +/** + * User: peter.lawrey + * Date: 05/08/13 + * Time: 19:06 + */ +public class RunningMinimum implements Differencer { + private final long actualMinimum; + private final int drift; + private long lastStartTime = Long.MIN_VALUE; + private long minimum = Long.MAX_VALUE; + + public RunningMinimum(long actualMinimum) { + this(actualMinimum, 100 * 1000); + } + + public RunningMinimum(long actualMinimum, int drift) { + this.actualMinimum = actualMinimum; + this.drift = drift; + } + + @Override + public long sample(long startTime, long endTime) { + if (lastStartTime + drift <= startTime) { + if (lastStartTime != Long.MIN_VALUE) + minimum += (startTime - lastStartTime) / drift; + lastStartTime = startTime; + } + long delta = endTime - startTime; + if (minimum > delta) + minimum = delta; + return delta - minimum + actualMinimum; + } + + public long minimum() { + return minimum; + } +} diff --git a/lang/src/main/java/net/openhft/lang/testing/VanillaDifferencer.java b/lang/src/main/java/net/openhft/lang/testing/VanillaDifferencer.java new file mode 100644 index 0000000..3853861 --- /dev/null +++ b/lang/src/main/java/net/openhft/lang/testing/VanillaDifferencer.java @@ -0,0 +1,29 @@ +/* + * 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.testing; + +/** + * User: peter.lawrey + * Date: 05/08/13 + * Time: 19:07 + */ +public class VanillaDifferencer implements Differencer { + @Override + public long sample(long startTime, long endTime) { + return endTime - startTime; + } +} diff --git a/lang/src/main/java/net/openhft/lang/thread/NamedThreadFactory.java b/lang/src/main/java/net/openhft/lang/thread/NamedThreadFactory.java new file mode 100644 index 0000000..fc9895e --- /dev/null +++ b/lang/src/main/java/net/openhft/lang/thread/NamedThreadFactory.java @@ -0,0 +1,51 @@ +/* + * 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.thread; + +import org.jetbrains.annotations.NotNull; + +import java.util.concurrent.ThreadFactory; +import java.util.concurrent.atomic.AtomicInteger; + +/** + * User: peter.lawrey Date: 18/08/13 Time: 11:37 + */ +public class NamedThreadFactory implements ThreadFactory { + private final AtomicInteger id = new AtomicInteger(); + private final String name; + private final Boolean daemon; + + public NamedThreadFactory(@NotNull String name) { + this(name, null); + } + + public NamedThreadFactory(@NotNull String name, Boolean daemon) { + this.name = name; + this.daemon = daemon; + } + + @NotNull + @Override + public Thread newThread(@NotNull Runnable r) { + int id = this.id.getAndIncrement(); + String nameN = id == 0 ? name : (name + '-' + id); + Thread t = new Thread(r, nameN); + if (daemon != null) + t.setDaemon(daemon); + return t; + } +} diff --git a/lang/src/main/java/net/openhft/lang/values/BooleanValue.java b/lang/src/main/java/net/openhft/lang/values/BooleanValue.java new file mode 100644 index 0000000..5af319c --- /dev/null +++ b/lang/src/main/java/net/openhft/lang/values/BooleanValue.java @@ -0,0 +1,28 @@ +/* + * 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.values; + +/** + * User: peter.lawrey + * Date: 10/10/13 + * Time: 07:19 + */ +public interface BooleanValue { + boolean getValue(); + + void setValue(boolean value); +} diff --git a/lang/src/main/java/net/openhft/lang/values/ByteValue.java b/lang/src/main/java/net/openhft/lang/values/ByteValue.java new file mode 100644 index 0000000..b0d1f62 --- /dev/null +++ b/lang/src/main/java/net/openhft/lang/values/ByteValue.java @@ -0,0 +1,30 @@ +/* + * 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.values; + +/** + * User: peter.lawrey + * Date: 10/10/13 + * Time: 07:19 + */ +public interface ByteValue { + byte getValue(); + + void setValue(byte value); + + byte addValue(byte b); +} diff --git a/lang/src/main/java/net/openhft/lang/values/CharValue.java b/lang/src/main/java/net/openhft/lang/values/CharValue.java new file mode 100644 index 0000000..d798208 --- /dev/null +++ b/lang/src/main/java/net/openhft/lang/values/CharValue.java @@ -0,0 +1,28 @@ +/* + * 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.values; + +/** + * User: peter.lawrey + * Date: 10/10/13 + * Time: 07:17 + */ +public interface CharValue { + char getValue(); + + void setValue(char value); +} diff --git a/lang/src/main/java/net/openhft/lang/values/DoubleValue.java b/lang/src/main/java/net/openhft/lang/values/DoubleValue.java new file mode 100644 index 0000000..8954f73 --- /dev/null +++ b/lang/src/main/java/net/openhft/lang/values/DoubleValue.java @@ -0,0 +1,32 @@ +/* + * 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.values; + +/** + * User: peter.lawrey + * Date: 10/10/13 + * Time: 07:12 + */ +public interface DoubleValue { + double getValue(); + + void setValue(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 new file mode 100644 index 0000000..b56152b --- /dev/null +++ b/lang/src/main/java/net/openhft/lang/values/FloatValue.java @@ -0,0 +1,32 @@ +/* + * 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.values; + +/** + * User: peter.lawrey + * Date: 10/10/13 + * Time: 07:15 + */ +public interface FloatValue { + float getValue(); + + void setValue(float value); + + 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 new file mode 100644 index 0000000..538ea42 --- /dev/null +++ b/lang/src/main/java/net/openhft/lang/values/Int24Value.java @@ -0,0 +1,33 @@ +/* + * 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.values; + +import net.openhft.lang.model.constraints.Range; + +/** + * 24-bit signed value + * User: peter.lawrey + * Date: 10/10/13 + * Time: 07:15 + */ +public interface Int24Value { + int getValue(); + + void setValue(@Range(min = -1 << 23, max = (1 << 23) - 1) int value); + + int addValue(int delta); +} diff --git a/lang/src/main/java/net/openhft/lang/values/Int48Value.java b/lang/src/main/java/net/openhft/lang/values/Int48Value.java new file mode 100644 index 0000000..bb7482b --- /dev/null +++ b/lang/src/main/java/net/openhft/lang/values/Int48Value.java @@ -0,0 +1,33 @@ +/* + * 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.values; + +import net.openhft.lang.model.constraints.Range; + +/** + * 48-bit signed value + * User: peter.lawrey + * Date: 10/10/13 + * Time: 07:15 + */ +public interface Int48Value { + long getValue(); + + void setValue(@Range(min = -1L << 47, max = (1L << 47) - 1) long value); + + long addValue(long delta); +} diff --git a/lang/src/main/java/net/openhft/lang/values/IntValue.java b/lang/src/main/java/net/openhft/lang/values/IntValue.java new file mode 100644 index 0000000..892d640 --- /dev/null +++ b/lang/src/main/java/net/openhft/lang/values/IntValue.java @@ -0,0 +1,43 @@ +/* + * 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.values; + +/** + * User: peter.lawrey + * Date: 10/10/13 + * Time: 07:15 + */ +public interface IntValue { + int getValue(); + + void setValue(int value); + + int addValue(int delta); + + int addAtomicValue(int delta); + + boolean compareAndSwapValue(int expected, int value); + + boolean tryLockValue(); + + boolean tryLockNanosValue(long nanos); + + void busyLockValue() throws InterruptedException, IllegalStateException; + + 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 new file mode 100644 index 0000000..b59b707 --- /dev/null +++ b/lang/src/main/java/net/openhft/lang/values/LongValue.java @@ -0,0 +1,34 @@ +/* + * 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.values; + +/** + * User: peter.lawrey + * Date: 10/10/13 + * Time: 07:11 + */ +public interface LongValue { + long getValue(); + + void setValue(long value); + + long addValue(long delta); + + long addAtomicValue(long delta); + + boolean compareAndSwapValue(long expected, long value); +} diff --git a/lang/src/main/java/net/openhft/lang/values/ShortValue.java b/lang/src/main/java/net/openhft/lang/values/ShortValue.java new file mode 100644 index 0000000..4dd5547 --- /dev/null +++ b/lang/src/main/java/net/openhft/lang/values/ShortValue.java @@ -0,0 +1,30 @@ +/* + * 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.values; + +/** + * User: peter.lawrey + * Date: 10/10/13 + * Time: 07:16 + */ +public interface ShortValue { + short getValue(); + + void setValue(short value); + + short addValue(short delta); +} diff --git a/lang/src/main/java/net/openhft/lang/values/StringValue.java b/lang/src/main/java/net/openhft/lang/values/StringValue.java new file mode 100644 index 0000000..2a0c587 --- /dev/null +++ b/lang/src/main/java/net/openhft/lang/values/StringValue.java @@ -0,0 +1,30 @@ +/* + * 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.values; + +import net.openhft.lang.model.constraints.MaxSize; + +/** + * User: peter.lawrey + * Date: 10/10/13 + * Time: 07:13 + */ +public interface StringValue { + String getValue(); + + void setValue(@MaxSize CharSequence value); +} diff --git a/lang/src/main/java/net/openhft/lang/values/UnsignedByteValue.java b/lang/src/main/java/net/openhft/lang/values/UnsignedByteValue.java new file mode 100644 index 0000000..7cc5007 --- /dev/null +++ b/lang/src/main/java/net/openhft/lang/values/UnsignedByteValue.java @@ -0,0 +1,32 @@ +/* + * 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.values; + +import net.openhft.lang.model.constraints.Range; + +/** + * User: peter.lawrey + * Date: 10/10/13 + * Time: 07:19 + */ +public interface UnsignedByteValue { + int getValue(); + + void setValue(@Range(min = 0, max = 255) int value); + + int addValue(int delta); +} diff --git a/lang/src/main/java/net/openhft/lang/values/UnsignedIntValue.java b/lang/src/main/java/net/openhft/lang/values/UnsignedIntValue.java new file mode 100644 index 0000000..8eae1a3 --- /dev/null +++ b/lang/src/main/java/net/openhft/lang/values/UnsignedIntValue.java @@ -0,0 +1,33 @@ +/* + * 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.values; + +import net.openhft.lang.model.constraints.Range; + +/** + * User: peter.lawrey + * Date: 10/10/13 + * Time: 07:19 + */ +public interface UnsignedIntValue { + long getValue(); + + 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 new file mode 100644 index 0000000..b9d06d6 --- /dev/null +++ b/lang/src/main/java/net/openhft/lang/values/UnsignedShortValue.java @@ -0,0 +1,32 @@ +/* + * 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.values; + +import net.openhft.lang.model.constraints.Range; + +/** + * User: peter.lawrey + * Date: 10/10/13 + * Time: 07:19 + */ +public interface UnsignedShortValue { + int getValue(); + + void setValue(@Range(min = 0, max = 65535) int value); + + int addValue(int delta); +} diff --git a/lang/src/test/java/net/openhft/lang/JvmTest.java b/lang/src/test/java/net/openhft/lang/JvmTest.java new file mode 100644 index 0000000..af3eae8 --- /dev/null +++ b/lang/src/test/java/net/openhft/lang/JvmTest.java @@ -0,0 +1,36 @@ +/* + * 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; + +import org.junit.Test; + +/** + * User: peter.lawrey + * Date: 20/09/13 + * Time: 10:04 + */ +public class JvmTest { + @Test + public void testIs64Bit() { + System.out.println("is64 = " + Jvm.is64Bit()); + } + + @Test + public void testGetProcessId() { + System.out.println("pid = " + Jvm.getProcessId()); + } +} diff --git a/lang/src/test/java/net/openhft/lang/MathsTest.java b/lang/src/test/java/net/openhft/lang/MathsTest.java new file mode 100644 index 0000000..ab96efb --- /dev/null +++ b/lang/src/test/java/net/openhft/lang/MathsTest.java @@ -0,0 +1,53 @@ +/* + * 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; + +import org.junit.Test; + +import java.math.BigDecimal; +import java.util.Random; + +import static org.junit.Assert.assertEquals; + +/** + * User: peter.lawrey + * Date: 20/09/13 + * Time: 10:31 + */ +public class MathsTest { + @Test + public void testIntLog2() { + for (int i = 0; i < 63; i++) { + long l = 1L << i; + assertEquals(i, Maths.intLog2(l)); + } + } + + @Test + public void testRounding() { + Random rand = new Random(1); + for (int i = 0; i < 1000; i++) { + double d = Math.pow(1e18, rand.nextDouble()) / 1e6; + BigDecimal bd = new BigDecimal(d); + assertEquals(bd.setScale(2, BigDecimal.ROUND_HALF_UP).doubleValue(), Maths.round2(d), 5e-2); + assertEquals(bd.setScale(4, BigDecimal.ROUND_HALF_UP).doubleValue(), Maths.round4(d), 5e-4); + assertEquals(bd.setScale(6, BigDecimal.ROUND_HALF_UP).doubleValue(), Maths.round6(d), 5e-6); + if (d < 1e8) + assertEquals(bd.setScale(8, BigDecimal.ROUND_HALF_UP).doubleValue(), Maths.round8(d), 5e-8); + } + } +} diff --git a/lang/src/test/java/net/openhft/lang/collection/HugeArrayTest.java b/lang/src/test/java/net/openhft/lang/collection/HugeArrayTest.java new file mode 100644 index 0000000..0b10e01 --- /dev/null +++ b/lang/src/test/java/net/openhft/lang/collection/HugeArrayTest.java @@ -0,0 +1,83 @@ +/* + * 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.collection; + +import net.openhft.lang.model.JavaBeanInterface; +import org.junit.Test; + +import static org.junit.Assert.assertEquals; + +/** + * User: peter.lawrey + * Date: 08/10/13 + * Time: 08:30 + */ +public class HugeArrayTest { + static void assertEquals2(long a, long b) { + if (a != b) + org.junit.Assert.assertEquals(a, b); + } + + @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); +// 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); + } + 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 new file mode 100644 index 0000000..2613063 --- /dev/null +++ b/lang/src/test/java/net/openhft/lang/collection/HugePricesMain.java @@ -0,0 +1,90 @@ +/* + * 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.collection; + +import net.openhft.lang.model.constraints.MaxSize; +import net.openhft.lang.model.constraints.Range; + +import static org.junit.Assert.assertEquals; + +interface Price { + String getInstrument(); + + void setInstrument(@MaxSize CharSequence instrument); + + TimedQuote getAsk(); + + void setAsk(TimedQuote ask); + + TimedQuote getBid(); + + void setBid(TimedQuote bid); +} + +interface QuoteHeader { + int getMajor(); + + // @Start + void setMajor(@Range(min = 0, max = (1 << 24) - 1) int major); + + void setType(char ch); + + char getType(); +} + +interface Quote extends QuoteHeader { + double getPrice(); + + void setPrice(double price); + + int getAmount(); + + void setAmount(int amount); +} + +interface TimedQuote extends Quote { + long getTimestamp(); + + void setTimestamp(long price); +} + +public class HugePricesMain { + public static void main(String[] args) { + int length = 1000; + final HugeArray<Price> prices = + HugeCollections.newArray(Price.class, length); + for (int i = 0; i < length; i++) { + final Price price = prices.get(i); + price.setInstrument("ID" + i); + price.getAsk().setPrice(100.1); + price.getAsk().setAmount(1000); + price.getBid().setPrice(99.8); + price.getBid().setAmount(2000); + prices.recycle(price); + } + for (int i = 0; i < length; i++) { + final Price price = prices.get(i); + 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(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 new file mode 100644 index 0000000..5c5d01c --- /dev/null +++ b/lang/src/test/java/net/openhft/lang/collection/HugeQueueTest.java @@ -0,0 +1,71 @@ +/* + * 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.collection; + +import net.openhft.lang.model.JavaBeanInterface; +import org.junit.Test; + +import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertTrue; + +/** + * User: peter.lawrey + * Date: 08/10/13 + * Time: 13:47 + */ +public class HugeQueueTest { + @Test + public void testQueue() { + // runs with a maximum heap size of 32 MB. + int tests = 5 * 1000 * 1000; + int length = 1000; + HugeQueue<JavaBeanInterface> queue = HugeCollections.newQueue(JavaBeanInterface.class, length); + long start = System.nanoTime(); + for (int j = 0; j < tests; j += length) { + for (int i = 0; i < length; i++) { + JavaBeanInterface jbi = queue.offer(); + jbi.setByte((byte) i); + jbi.setChar((char) i); + jbi.setShort((short) i); + jbi.setInt(i); + jbi.setFloat(i); + jbi.setLong(i); + jbi.setDouble(i); + jbi.setFlag((i & 3) == 0); + queue.recycle(jbi); + } + assertTrue(queue.isFull()); + for (int i = 0; i < length; i++) { + JavaBeanInterface jbi = queue.take(); + assertEquals((byte) i, jbi.getByte()); + assertEquals((char) i, jbi.getChar()); + assertEquals((short) i, jbi.getShort()); + assertEquals(i, jbi.getInt()); + assertEquals(i, jbi.getFloat(), 0); + assertEquals(i, jbi.getLong()); + assertEquals(i, jbi.getDouble(), 0.0); + assertEquals((i & 3) == 0, jbi.getFlag()); + queue.recycle(jbi); + } + assertTrue(queue.isEmpty()); + } + 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/io/AllocationRatesTest.java b/lang/src/test/java/net/openhft/lang/io/AllocationRatesTest.java new file mode 100644 index 0000000..da8696a --- /dev/null +++ b/lang/src/test/java/net/openhft/lang/io/AllocationRatesTest.java @@ -0,0 +1,81 @@ +/* + * 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.io; + +import org.junit.Test; + +import java.nio.ByteBuffer; + +/** + * User: peter 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 +buffers 128 KB took an average of 13,062 ns for heap ByteBuffer, 17,855 ns for direct ByteBuffer and 903 for DirectStore +buffers 128 KB took an average of 12,809 ns for heap ByteBuffer, 21,602 ns for direct ByteBuffer and 922 for DirectStore +buffers 128 KB took an average of 10,768 ns for heap ByteBuffer, 21,444 ns for direct ByteBuffer and 894 for DirectStore +buffers 128 KB took an average of 8,739 ns for heap ByteBuffer, 22,684 ns for direct ByteBuffer and 890 for DirectStore + */ +public class AllocationRatesTest { + static final int BUFFER_SIZE = 128 * 1024; + static final int ALLOCATIONS = 25000; + public static final int BATCH = 10; + + @Test + public void compareAllocationRates() { + for (int i = 0; i < 5; i++) { + long timeHBB = timeHeapByteBufferAllocations(); + long timeDBB = timeDirectByteBufferAllocations(); + long timeDS = timeDirectStoreAllocations(); + System.out.printf("buffers %d KB took an average of %,d ns for heap ByteBuffer, %,d ns for direct ByteBuffer and %,d for DirectStore%n", + BUFFER_SIZE / 1024, timeHBB / ALLOCATIONS, timeDBB / ALLOCATIONS, timeDS / ALLOCATIONS + ); + } + } + + private long timeHeapByteBufferAllocations() { + long start = System.nanoTime(); + for (int i = 0; i < ALLOCATIONS; i += BATCH) { + ByteBuffer[] bb = new ByteBuffer[BATCH]; + for (int j = 0; j < BATCH; j++) + bb[j] = ByteBuffer.allocate(BUFFER_SIZE); + } + return System.nanoTime() - start; + } + + private long timeDirectByteBufferAllocations() { + long start = System.nanoTime(); + for (int i = 0; i < ALLOCATIONS; i += BATCH) { + ByteBuffer[] bb = new ByteBuffer[BATCH]; + for (int j = 0; j < BATCH; j++) + bb[j] = ByteBuffer.allocateDirect(BUFFER_SIZE); + } + return System.nanoTime() - start; + } + + private long timeDirectStoreAllocations() { + long start = System.nanoTime(); + for (int i = 0; i < ALLOCATIONS; i += BATCH) { + DirectStore[] ds = new DirectStore[BATCH]; + for (int j = 0; j < BATCH; j++) + ds[j] = DirectStore.allocateLazy(BUFFER_SIZE); + for (int j = 0; j < BATCH; j++) + ds[j].free(); + } + 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 new file mode 100644 index 0000000..9c2ec03 --- /dev/null +++ b/lang/src/test/java/net/openhft/lang/io/ByteBufferBytesTest.java @@ -0,0 +1,920 @@ +/* + * 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.io; + +import net.openhft.lang.Maths; +import net.openhft.lang.thread.NamedThreadFactory; +import org.junit.Before; +import org.junit.Test; + +import java.io.*; +import java.math.BigDecimal; +import java.nio.ByteBuffer; +import java.nio.ByteOrder; +import java.text.SimpleDateFormat; +import java.util.*; +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 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. + */ +public class ByteBufferBytesTest { + public static final int SIZE = 128; + private ByteBufferBytes bytes; + private ByteBuffer byteBuffer; + + @Before + public void beforeTest() { + byteBuffer = ByteBuffer.allocate(SIZE).order(ByteOrder.nativeOrder()); + bytes = new ByteBufferBytes(byteBuffer); + } + + @Test + public void testLongHash() throws Exception { + byte[] bytes = {1, 2, 3, 4, 5, 6, 7, 8}; + long h = NativeBytes.longHash(bytes, 0, bytes.length); + assertFalse(h == 0); + byte[] bytes2 = {1, 2, 3, 4, 5, 6, 7, 8, 9}; + long h2 = NativeBytes.longHash(bytes2, 0, bytes2.length); + assertFalse(h2 == 0); + assertFalse(h2 == h); + } + + @Test + public void testRead() throws Exception { + for (int i = 0; i < bytes.capacity(); i++) + bytes.writeByte(i, i); + bytes.position(0); + for (int i = 0; i < bytes.capacity(); i++) + assertEquals((byte) i, bytes.read()); + for (int i = (int) (bytes.capacity() - 1); i >= 0; i--) { + assertEquals((byte) i, bytes.readByte(i)); + } + + } + + @Test + public void testReadFully() throws Exception { + for (int i = 0; i < bytes.capacity(); i++) + bytes.write(i); + bytes.position(0); + byte[] bytes = new byte[(int) this.bytes.capacity()]; + this.bytes.readFully(bytes); + for (int i = 0; i < this.bytes.capacity(); i++) + assertEquals((byte) i, bytes[i]); + } + + @Test + public void testCompareAndSetLong() throws Exception { + 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 { + for (int i = 0; i < bytes.capacity(); i++) + bytes.write(i); + for (int i = (int) (bytes.capacity() - 1); i >= 0; i--) { + bytes.position(i); + assertEquals((byte) i, bytes.read()); + } + } + + @Test + public void testCapacity() throws Exception { + assertEquals(SIZE, bytes.capacity()); + assertEquals(10, new NativeBytes(0, 0, 10).capacity()); + } + + @Test + public void testRemaining() throws Exception { + assertEquals(SIZE, bytes.remaining()); + bytes.position(10); + assertEquals(SIZE - 10, bytes.remaining()); + } + + @Test + public void testByteOrder() throws Exception { + assertEquals(ByteOrder.nativeOrder(), bytes.byteOrder()); + } + + @Test + public void testCheckEndOfBuffer() throws Exception { + bytes.checkEndOfBuffer(); + + bytes.position(SIZE + 2); + try { + bytes.checkEndOfBuffer(); + fail(); + } catch (IndexOutOfBoundsException expected) { + } + } + + @Test + public void testAppendDouble() { + testAppendDouble0(-6.895305375646115E24); + Random random = new Random(1); + for (int i = 0; i < 100000; i++) { + double d = Math.pow(1e32, random.nextDouble()) / 1e6; + if (i % 3 == 0) d = -d; + testAppendDouble0(d); + } + } + + private void testAppendDouble0(double d) { + bytes.position(0); + bytes.append(d).append(' '); + bytes.position(0); + double d2 = bytes.parseDouble(); + assertEquals(d, d2, 0); + } + + @Test + public void testAppendDouble2() { +// testAppendDouble0(-0.93879148954440506, 14); +// testAppendDouble0(-0.214980202661, 12); +// testAppendDouble0(-0.937082148896, 12); + testAppendDouble0(0.17805, 5); + Random random = new Random(1); + + for (int j = 0; j < 20000; j++) { + double d = random.nextDouble(); + if (j % 3 == 0) d = -d; +// if (j % 5 == 0) d *= 1e6; + for (int i = 0; i < 4; i++) { + testAppendDouble0(d, i); + } + } + } + + private void testAppendDouble0(double d, int precision) { + bytes.position(0); + bytes.append(d, precision).append(' '); + bytes.position(0); + String text = bytes.parseUTF(SPACE_STOP); + bytes.position(0); + assertEquals(0, bytes.position()); + double d2 = bytes.parseDouble(); + double d3 = (double) Math.round(d * Maths.power10(precision)) / Maths.power10(precision); +// if (precision >= 14) +// assertEquals("'" + text + "' p: " + precision + " v: " + new BigDecimal(d), d3, d2, 5e-29 * Maths.power10(precision)); +// else + assertEquals("'" + text + "' p: " + precision, d3, d2, 0); + } + + @Test + public void testWriteReadBytes() { + byte[] bytes = "Hello World!".getBytes(); + this.bytes.write(bytes); + byte[] bytes2 = new byte[bytes.length]; + this.bytes.position(0); + this.bytes.read(bytes2); + assertTrue(Arrays.equals(bytes, bytes2)); + + this.bytes.write(22, bytes); + byte[] bytes3 = new byte[bytes.length]; + this.bytes.skipBytes((int) (22 - this.bytes.position())); + assertEquals(bytes3.length, this.bytes.read(bytes3)); + assertTrue(Arrays.equals(bytes, bytes3)); + this.bytes.position(this.bytes.capacity()); + assertEquals(-1, this.bytes.read(bytes3)); + } + + @Test + public void testWriteReadUTFΔ() { + String[] words = "Hello,World!,Bye£€!".split(","); + for (String word : words) { + bytes.writeUTFΔ(word); + } + bytes.writeUTFΔ(null); + bytes.position(0); + for (String word : words) { + assertEquals(word, bytes.readUTFΔ()); + } + assertEquals(null, bytes.readUTFΔ()); + assertEquals("", bytes.readUTFΔ()); + assertEquals(26, bytes.position()); // check the size + + bytes.position(0); + StringBuilder sb = new StringBuilder(); + for (String word : words) { + assertTrue(bytes.readUTFΔ(sb)); + assertEquals(word, sb.toString()); + } + assertFalse(bytes.readUTFΔ(sb)); + assertTrue(bytes.readUTFΔ(sb)); + assertEquals("", sb.toString()); + } + + @Test + public void testWriteReadUTF() { + String[] words = "Hello,World!,Bye£€!".split(","); + for (String word : words) { + bytes.writeUTF(word); + } + bytes.writeUTF(""); + assertEquals(28, bytes.position()); // check the size, more bytes for less strings than writeUTFΔ + bytes.position(0); + for (String word : words) { + assertEquals(word, bytes.readUTF()); + } + assertEquals("", bytes.readUTF()); + } + + @Test + public void testAppendParseUTF() { + String[] words = "Hello,World!,Bye£€!".split(","); + for (String word : words) { + bytes.append(word).append('\t'); + } + bytes.append('\t'); + bytes.position(0); + for (String word : words) { + assertEquals(word, bytes.parseUTF(CONTROL_STOP)); + } + assertEquals("", bytes.parseUTF(CONTROL_STOP)); + + bytes.position(0); + StringBuilder sb = new StringBuilder(); + for (String word : words) { + bytes.parseUTF(sb, CONTROL_STOP); + assertEquals(word, sb.toString()); + } + bytes.parseUTF(sb, CONTROL_STOP); + assertEquals("", sb.toString()); + + bytes.position(0); + bytes.skipTo(CONTROL_STOP); + 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()); + + bytes.position(0); + bytes.stepBackAndSkipTo(CONTROL_STOP); + assertEquals(6, bytes.position()); + bytes.stepBackAndSkipTo(CONTROL_STOP); + assertEquals(6, bytes.position()); + bytes.position(10); + bytes.stepBackAndSkipTo(CONTROL_STOP); + assertEquals(13, bytes.position()); + + } + + @Test + public void testWriteReadLines() { + byte[] bytes = "Hello\nWorld!\r\nBye".getBytes(); + this.bytes.write(bytes); + this.bytes.position(0); + assertEquals("Hello", this.bytes.readLine()); + assertEquals("World!", this.bytes.readLine()); + assertTrue(this.bytes.readLine().startsWith("Bye")); + } + + @Test + public void testWriteReadByteBuffer() { + byte[] bytes = "Hello\nWorld!\r\nBye".getBytes(); + this.bytes.write(ByteBuffer.wrap(bytes)); + this.bytes.position(0); + byte[] bytes2 = new byte[bytes.length + 1]; + ByteBuffer bb2 = ByteBuffer.wrap(bytes2); + this.bytes.read(bb2); + + assertEquals(bytes2.length, bb2.position()); + assertTrue(Arrays.equals(bytes, Arrays.copyOf(bytes2, bytes.length))); + } + + @Test + public void testReadWriteBoolean() { + for (int i = 0; i < 32; i++) + bytes.writeBoolean(i, (i & 3) == 0); + bytes.position(32); + for (int i = 32; i < 64; i++) + bytes.writeBoolean((i & 5) == 0); + bytes.position(0); + for (int i = 0; i < 32; i++) + assertEquals((i & 3) == 0, bytes.readBoolean()); + for (int i = 32; i < 64; i++) + assertEquals((i & 5) == 0, bytes.readBoolean(i)); + } + + @Test + public void testReadWriteShort() { + for (int i = 0; i < 32; i += 2) + bytes.writeShort(i, i); + bytes.position(32); + for (int i = 32; i < 64; i += 2) + bytes.writeShort(i); + bytes.position(0); + for (int i = 0; i < 32; i += 2) + assertEquals(i, bytes.readShort()); + for (int i = 32; i < 64; i += 2) + assertEquals(i, bytes.readShort(i)); + } + + @Test + public void testReadWriteCompactShort() { + int[] ints = {Short.MIN_VALUE, Short.MAX_VALUE, -125, 0, 127, -10000, 10000}; + for (int i : ints) { + bytes.writeCompactShort(i); +// System.out.println(i + " " + bytes.position()); + } + assertEquals(5 + 2 * 3, bytes.position()); + + bytes.position(0); + for (int i : ints) + assertEquals(i, bytes.readCompactShort()); + } + + @Test + public void testReadWriteCompactInt() { + int[] ints = {-10000000, Integer.MIN_VALUE, Integer.MAX_VALUE, Short.MIN_VALUE + 3, 0, Short.MAX_VALUE, 10000000}; + for (int i : ints) { + bytes.writeCompactInt(i); +// System.out.println(i + " " + bytes.position()); + } + assertEquals(5 * 2 + 2 * 6, bytes.position()); + + bytes.position(0); + for (int i : ints) + assertEquals(i, bytes.readCompactInt()); + } + + @Test + public void testReadWriteCompactLong() { + long[] ints = {Long.MAX_VALUE, -100000000000L, Long.MIN_VALUE, Integer.MIN_VALUE + 3, 0, Integer.MAX_VALUE, 100000000000L}; + for (long i : ints) { + bytes.writeCompactLong(i); +// System.out.println(i + " " + bytes.position()); + } + assertEquals(5 * 4 + 2 * 12, bytes.position()); + + bytes.position(0); + for (long i : ints) + assertEquals(i, bytes.readCompactLong()); + } + + @Test + public void testReadWriteCompactDouble() { + double[] doubles = {1, 1000, 1000000, -100000000, 0.1f, 0.1, 0.5, 0.51}; + for (double i : doubles) { + bytes.writeCompactDouble(i); +// System.out.println(i + " " + bytes.position()); + } + assertEquals(6 * 4 + 2 * 12, bytes.position()); + + bytes.position(0); + for (double i : doubles) + assertEquals(i, bytes.readCompactDouble(), 0.0); + } + + @Test + public void testReadWriteStop() { + long[] longs = {Long.MIN_VALUE, Long.MAX_VALUE, Integer.MIN_VALUE, Integer.MAX_VALUE}; + for (long i : longs) { + bytes.writeStopBit(i); +// System.out.println(i + " " + bytes.position()); + } + assertEquals(9 + 10, +5 + 6, bytes.position()); + + bytes.position(0); + for (long i : longs) + assertEquals(i, bytes.readStopBit()); + } + + @Test + public void testReadWriteChar() { + for (int i = 0; i < 32; i += 2) + bytes.writeChar(i, i); + bytes.position(32); + for (int i = 32; i < 64; i += 2) + bytes.writeChar(i); + bytes.position(0); + for (int i = 0; i < 32; i += 2) + assertEquals(i, bytes.readChar()); + for (int i = 32; i < 64; i += 2) + assertEquals(i, bytes.readChar(i)); + } + + @Test + public void testReadWriteUnsignedShort() { + for (int i = 0; i < 32; i += 2) + bytes.writeUnsignedShort(i, ~i); + bytes.position(32); + for (int i = 32; i < 64; i += 2) + bytes.writeUnsignedShort(~i); + bytes.position(0); + for (int i = 0; i < 32; i += 2) + assertEquals(~i & 0xFFFF, bytes.readUnsignedShort()); + for (int i = 32; i < 64; i += 2) + assertEquals(~i & 0xFFFF, bytes.readUnsignedShort(i)); + } + + @Test + public void testReadWriteInt() { + for (int i = 0; i < 32; i += 4) + bytes.writeInt(i, i); + bytes.position(32); + for (int i = 32; i < 64; i += 4) + bytes.writeInt(i); + bytes.position(0); + for (int i = 0; i < 32; i += 4) + assertEquals(i, bytes.readInt()); + for (int i = 32; i < 64; i += 4) + assertEquals(i, bytes.readInt(i)); + } + + @Test + public void testReadWriteThreadeSafeInt() { + for (int i = 0; i < 32; i += 4) + bytes.writeOrderedInt(i, i); + bytes.position(32); + for (int i = 32; i < 64; i += 4) + bytes.writeOrderedInt(i); + bytes.position(0); + for (int i = 0; i < 32; i += 4) + assertEquals(i, bytes.readVolatileInt()); + for (int i = 32; i < 64; i += 4) + assertEquals(i, bytes.readVolatileInt(i)); + } + + @Test + public void testReadWriteFloat() { + for (int i = 0; i < 32; i += 4) + bytes.writeFloat(i, i); + bytes.position(32); + for (int i = 32; i < 64; i += 4) + bytes.writeFloat(i); + bytes.position(0); + for (int i = 0; i < 32; i += 4) + assertEquals(i, bytes.readFloat(), 0); + for (int i = 32; i < 64; i += 4) + assertEquals(i, bytes.readFloat(i), 0); + } + + @Test + public void testReadWriteUnsignedInt() { + for (int i = 0; i < 32; i += 4) + bytes.writeUnsignedInt(i, ~i); + bytes.position(32); + for (int i = 32; i < 64; i += 4) + bytes.writeUnsignedInt(~i); + bytes.position(0); + for (int i = 0; i < 32; i += 4) + assertEquals(~i & 0xFFFFFFFFL, bytes.readUnsignedInt()); + for (int i = 32; i < 64; i += 4) + assertEquals(~i & 0xFFFFFFFFL, bytes.readUnsignedInt(i)); + } + + @Test + public void testReadWriteInt24() { + for (int i = 0; i < 30; i += 3) + bytes.writeInt24(i, ~i & 0x7FFFFF); + bytes.position(30); + for (int i = 30; i < 63; i += 3) + bytes.writeInt24(~i & 0x7FFFFF); + assertEquals(63, bytes.position()); + bytes.position(0); + for (int i = 0; i < 30; i += 3) + assertEquals("i: " + i, ~i & 0x7FFFFFL, bytes.readInt24()); + for (int i = 30; i < 63; i += 3) + assertEquals("i: " + i, ~i & 0x7FFFFFL, bytes.readInt24(i)); + // now negative + bytes.position(0); + for (int i = 0; i < 30; i += 3) + bytes.writeInt24(i, ~i); + bytes.position(30); + for (int i = 30; i < 63; i += 3) + bytes.writeInt24(~i); + assertEquals(63, bytes.position()); + bytes.position(0); + for (int i = 0; i < 30; i += 3) + assertEquals("i: " + i, ~i << 8 >> 8, bytes.readInt24()); + for (int i = 30; i < 63; i += 3) + assertEquals("i: " + i, ~i << 8 >> 8, bytes.readInt24(i)); + } + + @Test + public void testReadWriteInt48() { + for (long i = 0; i < 30; i += 6) + bytes.writeInt48(i, ~i & 0x7FFFFFFFFFFFL); + bytes.position(30); + for (long i = 30; i < 60; i += 6) + bytes.writeInt48(~i & 0x7FFFFFFFFFFFL); + assertEquals(60, bytes.position()); + bytes.position(0); + for (long i = 0; i < 30; i += 6) + assertEquals("i: " + i, ~i & 0x7FFFFFFFFFFFL, bytes.readInt48()); + for (long i = 30; i < 60; i += 6) + assertEquals("i: " + i, ~i & 0x7FFFFFFFFFFFL, bytes.readInt48(i)); + // now negative + bytes.position(0); + for (long i = 0; i < 30; i += 6) + bytes.writeInt48(i, ~i); + bytes.position(30); + for (long i = 30; i < 60; i += 6) + bytes.writeInt48(~i); + assertEquals(60, bytes.position()); + bytes.position(0); + for (long i = 0; i < 30; i += 6) + assertEquals("i: " + i, ~i << 16 >> 16, bytes.readInt48()); + for (long i = 30; i < 60; i += 6) + assertEquals("i: " + i, ~i << 16 >> 16, bytes.readInt48(i)); + } + + @Test + public void testDateTimes() { + long now = System.currentTimeMillis(); + bytes.appendDateTimeMillis(now); + bytes.append(' '); + bytes.appendDateMillis(now); + bytes.append('T'); + bytes.appendTimeMillis(now % 86400000L); + assertEquals(23 * 2 + 1, bytes.position()); + bytes.position(0); + 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)); + } + + @Test + public void testReadWriteCompactUnsignedShort() { + for (int i = 0; i < 64; i += 4) + bytes.writeCompactUnsignedShort(i); + assertEquals(64 / 4, bytes.position()); + bytes.position(0); + for (int i = 0; i < 64; i += 4) + assertEquals(i, bytes.readByte(i / 4)); + for (int i = 0; i < 64; i += 4) + assertEquals(i, bytes.readCompactUnsignedShort()); + } + + @Test + public void testReadWriteCompactUnsignedInt() { + for (int i = 0; i < 64; i += 4) + bytes.writeCompactUnsignedInt(i); + assertEquals(64 / 2, bytes.position()); + bytes.position(0); + for (int i = 0; i < 64; i += 4) + assertEquals(i, bytes.readShort(i / 2)); + for (int i = 0; i < 64; i += 4) + assertEquals(i, bytes.readCompactUnsignedInt()); + } + + @Test + public void testReadWriteLong() { + for (long i = 0; i < 32; i += 8) + bytes.writeLong(i, i); + bytes.position(32); + for (long i = 32; i < 64; i += 8) + bytes.writeLong(i); + bytes.position(0); + for (long i = 0; i < 32; i += 8) + assertEquals(i, bytes.readLong()); + for (long i = 32; i < 64; i += 8) + assertEquals(i, bytes.readLong(i)); + } + + @Test + public void testReadWriteThreadSafeLong() { + for (long i = 0; i < 32; i += 8) + bytes.writeOrderedLong(i, i); + bytes.position(32); + for (long i = 32; i < 64; i += 8) + bytes.writeOrderedLong(i); + bytes.position(0); + for (long i = 0; i < 32; i += 8) + assertEquals(i, bytes.readVolatileLong()); + for (long i = 32; i < 64; i += 8) + assertEquals(i, bytes.readVolatileLong(i)); + } + + @Test + public void testReadWriteDouble() { + for (long i = 0; i < 32; i += 8) + bytes.writeDouble(i, i); + bytes.position(32); + for (long i = 32; i < 64; i += 8) + bytes.writeDouble(i); + bytes.position(0); + for (long i = 0; i < 32; i += 8) + assertEquals(i, bytes.readDouble(), 0); + for (long i = 32; i < 64; i += 8) + assertEquals(i, bytes.readDouble(i), 0); + } + + @Test + public void testAppendSubstring() { + bytes.append("Hello World", 2, 7).append("\n"); + bytes.position(0); + assertEquals("Hello World".substring(2, 7), bytes.parseUTF(CONTROL_STOP)); + } + + @Test + public void testWriteReadEnum() { + bytes.append(BuySell.Buy).append("\t").append(BuySell.Sell); + bytes.position(0); + assertEquals(BuySell.Buy, bytes.parseEnum(BuySell.class, CONTROL_STOP)); + assertEquals(BuySell.Sell, bytes.parseEnum(BuySell.class, CONTROL_STOP)); + assertEquals(null, bytes.parseEnum(BuySell.class, CONTROL_STOP)); + } + + @Test + public void testAppendParse() { + bytes.append(false).append(' '); + bytes.append(true).append(' '); + bytes.append("what?").append(' '); + bytes.append("word£€").append(' '); + bytes.append(BuySell.Buy).append(' '); + 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(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 testWriteByteChar() throws UnsupportedEncodingException { + bytes.writeBytes("Hello \u00ff\u01fe\u02fc\n"); + bytes.writeChars("Hello \u00ff\u01fe\u02fc\n"); + byte[] bytes = new byte[(int) this.bytes.position()]; + this.bytes.position(0); + this.bytes.readFully(bytes); + assertEquals("Hello \u00ff\u00fe\u00fc\n" + + "H\u0000e\u0000l\u0000l\u0000o\u0000 \u0000ÿ\u0000þ\u0001ü\u0002\n" + + "\u0000", new String(bytes, "ISO-8859-1")); + } + + @Test + 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.position(0); + assertEquals("Hell0 worl bye", bytes.parseUTF(CONTROL_STOP)); + } + + @Test + public void testAppendIterable() { + 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)); + } + + @Test + public void readWriteMutableDecimal() { + Random rand = new Random(2); + MutableDecimal md = new MutableDecimal(); + MutableDecimal md2 = new MutableDecimal(); +// md.set(1260042744, 0); + + for (int i = 0; i < 20000; i++) { + int n = rand.nextInt(); + for (int j = 0; j < 6; j++) { + testDecimal0(md, md2, n, j); + } + } + } + + private void testDecimal0(MutableDecimal md, MutableDecimal md2, int n, int j) { + md.set(n, j); + bytes.position(0); + bytes.append(md).append('\n'); + bytes.position(0); + bytes.parseDecimal(md2); + bytes.position(0); + String text = bytes.parseUTF(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)); + GZIPOutputStream out = new GZIPOutputStream(bytes.outputStream()); + out.write("Hello world\n".getBytes()); + out.close(); + bytes.position(0); + GZIPInputStream in = new GZIPInputStream(bytes.inputStream()); + byte[] bytes = new byte[12]; + for (int i = 0; i < 12; i++) + bytes[i] = (byte) in.read(); + assertEquals(-1, in.read()); + assertEquals("Hello world\n", new String(bytes)); + in.close(); + } + + @Test + public void testStream2() throws IOException { + OutputStream out = bytes.outputStream(); + out.write(11); + out.write(22); + out.write(33); + out.write(44); + out.write(55); + + bytes.position(0); + InputStream in = bytes.inputStream(); + assertTrue(in.markSupported()); + assertEquals(11, in.read()); + in.mark(1); + assertEquals(1, bytes.position()); + assertEquals(22, in.read()); + assertEquals(2, bytes.position()); + + assertEquals(33, in.read()); + in.reset(); + + assertEquals(1, bytes.position()); + assertEquals(22, in.read()); + + assertEquals(2, in.skip(2)); + assertEquals(4, bytes.position()); + assertEquals(SIZE - 4, bytes.available()); + assertEquals(55, in.read()); + in.close(); + } + + @Test + public void testWriteObject() { + for (Object o : new Object[]{10, 9.9, "string", new Date(), BigDecimal.valueOf(1.1)}) { + bytes.position(0); + bytes.writeObject(o); +// System.out.println(o +" size: "+bytes.position()); + assertTrue(bytes.position() < 21); + bytes.position(0); + Object o2 = bytes.readObject(); + bytes.position(0); + Object o3 = bytes.readObject(o.getClass()); + assertEquals(o, o2); + assertEquals(o, o3); + } + } + + @Test + public void testWriteSerializable() { + int capacity = 16 * 1024; + byteBuffer = ByteBuffer.allocateDirect(capacity); + bytes = new ByteBufferBytes(byteBuffer); + Calendar cal = Calendar.getInstance(); + bytes.writeObject(cal); + Dummy d = new Dummy(); + bytes.writeObject(d); + bytes.position(0); + Calendar cal2 = bytes.readObject(Calendar.class); + Dummy d2 = bytes.readObject(Dummy.class); + assertEquals(cal, cal2); + assertEquals(d, d2); + } + + @Test + public void testAddAndGet() { + for (int i = 0; i < 10; i++) + bytes.addAndGetInt(0L, 10); + assertEquals(100, bytes.readInt(0L)); + assertEquals(0, bytes.readInt(4L)); + + for (int i = 0; i < 11; i++) + bytes.getAndAdd(4L, 11); + assertEquals(100, bytes.readInt(0L)); + assertEquals(11 * 11, bytes.readInt(4L)); + } + + enum BuySell { + Buy, Sell + } + + static class Dummy implements Serializable { + @Override + public boolean equals(Object obj) { + return obj instanceof Dummy; + } + } + + @Test + public void testErrors() { + int capacity = 1024; + byteBuffer = ByteBuffer.allocate(capacity); + // it is actually much bigger than it believes + bytes = new ByteBufferBytes(byteBuffer, 0, 16); + bytes.writeLong(8); + assertFalse(bytes.isFinished()); + bytes.finish(); + assertTrue(bytes.isFinished()); + bytes.flush(); + bytes.writeLong(16); + bytes.finish(); + bytes.flush(); + try { + bytes.writeLong(24); + fail(); + } catch (IndexOutOfBoundsException expected) { + } + bytes.finish(); + bytes.flush(); + + bytes.reset(); + assertEquals(0, bytes.position()); + assertEquals(8, bytes.skip(8)); + assertEquals(8, bytes.position()); + bytes.writeLong(22); + bytes.close(); + } + + @Test + public void testWriteList() { + List<Integer> ints = Arrays.asList(1, 2, 3, 4); + bytes.writeList(ints); + bytes.reset(); + List<Integer> ints2 = new ArrayList<Integer>(); + bytes.readList(ints2, Integer.class); + assertEquals(ints, ints2); + + bytes.reset(); + List<String> words = Arrays.asList("Hello word byte for now".split(" ")); + bytes.writeList(words); + bytes.reset(); + List<String> words2 = new ArrayList<String>(); + bytes.readList(words2, String.class); + } + + @Test + public void testWriteMap() { + Map<String, Integer> map = new LinkedHashMap<String, Integer>() { + { + put("one", 1); + put("two", 2); + put("three", 3); + put("four", 4); + } + }; + + bytes.writeMap(map); + bytes.finish(); + + bytes.reset(); + Map<String, Integer> map2 = new LinkedHashMap<String, Integer>(); + bytes.readMap(map2, String.class, Integer.class); + assertEquals(map, map2); + } + + @Test + public void unloadFailed() throws InterruptedException { + bytes.busyLockInt(0); + ExecutorService es = Executors.newSingleThreadExecutor(new NamedThreadFactory("unloadFailed")); + Future<Void> future = es.submit(new Callable<Void>() { + @Override + public Void call() throws Exception { + bytes.unlockInt(0); + return null; + } + }); + es.shutdown(); + try { + future.get(); + fail(); + } catch (ExecutionException e) { + assertEquals(IllegalMonitorStateException.class, e.getCause().getClass()); + } + } +} diff --git a/lang/src/test/java/net/openhft/lang/io/DirectByteBufferBytesTest.java b/lang/src/test/java/net/openhft/lang/io/DirectByteBufferBytesTest.java new file mode 100644 index 0000000..bba0933 --- /dev/null +++ b/lang/src/test/java/net/openhft/lang/io/DirectByteBufferBytesTest.java @@ -0,0 +1,921 @@ +/* + * 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.io; + +import net.openhft.lang.Maths; +import net.openhft.lang.thread.NamedThreadFactory; +import org.junit.Before; +import org.junit.Test; + +import java.io.*; +import java.math.BigDecimal; +import java.nio.ByteBuffer; +import java.nio.ByteOrder; +import java.text.SimpleDateFormat; +import java.util.*; +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 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. + */ +public class DirectByteBufferBytesTest { + public static final int SIZE = 128; + private ByteBufferBytes bytes; + private ByteBuffer byteBuffer; + + @Before + public void beforeTest() { + byteBuffer = ByteBuffer.allocateDirect(SIZE).order(ByteOrder.nativeOrder()); + bytes = new ByteBufferBytes(byteBuffer); + } + + @Test + public void testLongHash() throws Exception { + byte[] bytes = {1, 2, 3, 4, 5, 6, 7, 8}; + long h = NativeBytes.longHash(bytes, 0, bytes.length); + assertFalse(h == 0); + byte[] bytes2 = {1, 2, 3, 4, 5, 6, 7, 8, 9}; + long h2 = NativeBytes.longHash(bytes2, 0, bytes2.length); + assertFalse(h2 == 0); + assertFalse(h2 == h); + } + + @Test + public void testRead() throws Exception { + for (int i = 0; i < bytes.capacity(); i++) + bytes.writeByte(i, i); + bytes.position(0); + for (int i = 0; i < bytes.capacity(); i++) + assertEquals((byte) i, bytes.read()); + for (int i = (int) (bytes.capacity() - 1); i >= 0; i--) { + assertEquals((byte) i, bytes.readByte(i)); + } + + } + + @Test + public void testReadFully() throws Exception { + for (int i = 0; i < bytes.capacity(); i++) + bytes.write(i); + bytes.position(0); + byte[] bytes = new byte[(int) this.bytes.capacity()]; + this.bytes.readFully(bytes); + for (int i = 0; i < this.bytes.capacity(); i++) + assertEquals((byte) i, bytes[i]); + } + + @Test + public void testCompareAndSetLong() throws Exception { + 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 { + for (int i = 0; i < bytes.capacity(); i++) + bytes.write(i); + for (int i = (int) (bytes.capacity() - 1); i >= 0; i--) { + bytes.position(i); + assertEquals((byte) i, bytes.read()); + } + } + + @Test + public void testCapacity() throws Exception { + assertEquals(SIZE, bytes.capacity()); + assertEquals(10, new NativeBytes(0, 0, 10).capacity()); + } + + @Test + public void testRemaining() throws Exception { + assertEquals(SIZE, bytes.remaining()); + bytes.position(10); + assertEquals(SIZE - 10, bytes.remaining()); + } + + @Test + public void testByteOrder() throws Exception { + assertEquals(ByteOrder.nativeOrder(), bytes.byteOrder()); + } + + @Test + public void testCheckEndOfBuffer() throws Exception { + bytes.checkEndOfBuffer(); + + bytes.position(SIZE + 2); + try { + bytes.checkEndOfBuffer(); + fail(); + } catch (IndexOutOfBoundsException expected) { + } + } + + @Test + public void testAppendDouble() { + testAppendDouble0(-6.895305375646115E24); + Random random = new Random(1); + for (int i = 0; i < 100000; i++) { + double d = Math.pow(1e32, random.nextDouble()) / 1e6; + if (i % 3 == 0) d = -d; + testAppendDouble0(d); + } + } + + private void testAppendDouble0(double d) { + bytes.position(0); + bytes.append(d).append(' '); + bytes.position(0); + double d2 = bytes.parseDouble(); + assertEquals(d, d2, 0); + } + + @Test + public void testAppendDouble2() { +// testAppendDouble0(-0.93879148954440506, 14); +// testAppendDouble0(-0.214980202661, 12); +// testAppendDouble0(-0.937082148896, 12); + testAppendDouble0(0.17805, 5); + Random random = new Random(1); + + for (int j = 0; j < 20000; j++) { + double d = random.nextDouble(); + if (j % 3 == 0) d = -d; +// if (j % 5 == 0) d *= 1e6; + for (int i = 0; i < 4; i++) { + testAppendDouble0(d, i); + } + } + } + + private void testAppendDouble0(double d, int precision) { + bytes.position(0); + bytes.append(d, precision).append(' '); + bytes.position(0); + String text = bytes.parseUTF(SPACE_STOP); + bytes.position(0); + assertEquals(0, bytes.position()); + double d2 = bytes.parseDouble(); + double d3 = (double) Math.round(d * Maths.power10(precision)) / Maths.power10(precision); +// if (precision >= 14) +// assertEquals("'" + text + "' p: " + precision + " v: " + new BigDecimal(d), d3, d2, 5e-29 * Maths.power10(precision)); +// else + assertEquals("'" + text + "' p: " + precision, d3, d2, 0); + } + + @Test + public void testWriteReadBytes() { + byte[] bytes = "Hello World!".getBytes(); + this.bytes.write(bytes); + byte[] bytes2 = new byte[bytes.length]; + this.bytes.position(0); + this.bytes.read(bytes2); + assertTrue(Arrays.equals(bytes, bytes2)); + + this.bytes.write(22, bytes); + byte[] bytes3 = new byte[bytes.length]; + this.bytes.skipBytes((int) (22 - this.bytes.position())); + assertEquals(bytes3.length, this.bytes.read(bytes3)); + assertTrue(Arrays.equals(bytes, bytes3)); + this.bytes.position(this.bytes.capacity()); + assertEquals(-1, this.bytes.read(bytes3)); + } + + @Test + public void testWriteReadUTFΔ() { + String[] words = "Hello,World!,Bye£€!".split(","); + for (String word : words) { + bytes.writeUTFΔ(word); + } + bytes.writeUTFΔ(null); + bytes.position(0); + for (String word : words) { + assertEquals(word, bytes.readUTFΔ()); + } + assertEquals(null, bytes.readUTFΔ()); + assertEquals("", bytes.readUTFΔ()); + assertEquals(26, bytes.position()); // check the size + + bytes.position(0); + StringBuilder sb = new StringBuilder(); + for (String word : words) { + assertTrue(bytes.readUTFΔ(sb)); + assertEquals(word, sb.toString()); + } + assertFalse(bytes.readUTFΔ(sb)); + assertTrue(bytes.readUTFΔ(sb)); + assertEquals("", sb.toString()); + } + + @Test + public void testWriteReadUTF() { + String[] words = "Hello,World!,Bye£€!".split(","); + for (String word : words) { + bytes.writeUTF(word); + } + bytes.writeUTF(""); + assertEquals(28, bytes.position()); // check the size, more bytes for less strings than writeUTFΔ + bytes.position(0); + for (String word : words) { + assertEquals(word, bytes.readUTF()); + } + assertEquals("", bytes.readUTF()); + } + + @Test + public void testAppendParseUTF() { + String[] words = "Hello,World!,Bye£€!".split(","); + for (String word : words) { + bytes.append(word).append('\t'); + } + bytes.append('\t'); + bytes.position(0); + for (String word : words) { + assertEquals(word, bytes.parseUTF(CONTROL_STOP)); + } + assertEquals("", bytes.parseUTF(CONTROL_STOP)); + + bytes.position(0); + StringBuilder sb = new StringBuilder(); + for (String word : words) { + bytes.parseUTF(sb, CONTROL_STOP); + assertEquals(word, sb.toString()); + } + bytes.parseUTF(sb, CONTROL_STOP); + assertEquals("", sb.toString()); + + bytes.position(0); + bytes.skipTo(CONTROL_STOP); + 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()); + + bytes.position(0); + bytes.stepBackAndSkipTo(CONTROL_STOP); + assertEquals(6, bytes.position()); + bytes.stepBackAndSkipTo(CONTROL_STOP); + assertEquals(6, bytes.position()); + bytes.position(10); + bytes.stepBackAndSkipTo(CONTROL_STOP); + assertEquals(13, bytes.position()); + + } + + @Test + public void testWriteReadLines() { + byte[] bytes = "Hello\nWorld!\r\nBye".getBytes(); + this.bytes.write(bytes); + this.bytes.position(0); + assertEquals("Hello", this.bytes.readLine()); + assertEquals("World!", this.bytes.readLine()); + assertTrue(this.bytes.readLine().startsWith("Bye")); + } + + @Test + public void testWriteReadByteBuffer() { + byte[] bytes = "Hello\nWorld!\r\nBye".getBytes(); + this.bytes.write(ByteBuffer.wrap(bytes)); + this.bytes.position(0); + byte[] bytes2 = new byte[bytes.length + 1]; + ByteBuffer bb2 = ByteBuffer.wrap(bytes2); + this.bytes.read(bb2); + + assertEquals(bytes2.length, bb2.position()); + assertTrue(Arrays.equals(bytes, Arrays.copyOf(bytes2, bytes.length))); + } + + @Test + public void testReadWriteBoolean() { + for (int i = 0; i < 32; i++) + bytes.writeBoolean(i, (i & 3) == 0); + bytes.position(32); + for (int i = 32; i < 64; i++) + bytes.writeBoolean((i & 5) == 0); + bytes.position(0); + for (int i = 0; i < 32; i++) + assertEquals((i & 3) == 0, bytes.readBoolean()); + for (int i = 32; i < 64; i++) + assertEquals((i & 5) == 0, bytes.readBoolean(i)); + } + + @Test + public void testReadWriteShort() { + for (int i = 0; i < 32; i += 2) + bytes.writeShort(i, i); + bytes.position(32); + for (int i = 32; i < 64; i += 2) + bytes.writeShort(i); + bytes.position(0); + for (int i = 0; i < 32; i += 2) + assertEquals(i, bytes.readShort()); + for (int i = 32; i < 64; i += 2) + assertEquals(i, bytes.readShort(i)); + } + + @Test + public void testReadWriteCompactShort() { + int[] ints = {Short.MIN_VALUE, Short.MAX_VALUE, -125, 0, 127, -10000, 10000}; + for (int i : ints) { + bytes.writeCompactShort(i); +// System.out.println(i + " " + bytes.position()); + } + assertEquals(5 + 2 * 3, bytes.position()); + + bytes.position(0); + for (int i : ints) + assertEquals(i, bytes.readCompactShort()); + } + + @Test + public void testReadWriteCompactInt() { + int[] ints = {-10000000, Integer.MIN_VALUE, Integer.MAX_VALUE, Short.MIN_VALUE + 3, 0, Short.MAX_VALUE, 10000000}; + for (int i : ints) { + bytes.writeCompactInt(i); +// System.out.println(i + " " + bytes.position()); + } + assertEquals(5 * 2 + 2 * 6, bytes.position()); + + bytes.position(0); + for (int i : ints) + assertEquals(i, bytes.readCompactInt()); + } + + @Test + public void testReadWriteCompactLong() { + long[] ints = {Long.MAX_VALUE, -100000000000L, Long.MIN_VALUE, Integer.MIN_VALUE + 3, 0, Integer.MAX_VALUE, 100000000000L}; + for (long i : ints) { + bytes.writeCompactLong(i); +// System.out.println(i + " " + bytes.position()); + } + assertEquals(5 * 4 + 2 * 12, bytes.position()); + + bytes.position(0); + for (long i : ints) + assertEquals(i, bytes.readCompactLong()); + } + + @Test + public void testReadWriteCompactDouble() { + double[] doubles = {1, 1000, 1000000, -100000000, 0.1f, 0.1, 0.5, 0.51}; + for (double i : doubles) { + bytes.writeCompactDouble(i); +// System.out.println(i + " " + bytes.position()); + } + assertEquals(6 * 4 + 2 * 12, bytes.position()); + + bytes.position(0); + for (double i : doubles) + assertEquals(i, bytes.readCompactDouble(), 0.0); + } + + @Test + public void testReadWriteStop() { + long[] longs = {Long.MIN_VALUE, Long.MAX_VALUE, Integer.MIN_VALUE, Integer.MAX_VALUE}; + for (long i : longs) { + bytes.writeStopBit(i); +// System.out.println(i + " " + bytes.position()); + } + assertEquals(9 + 10, +5 + 6, bytes.position()); + + bytes.position(0); + for (long i : longs) + assertEquals(i, bytes.readStopBit()); + } + + @Test + public void testReadWriteChar() { + for (int i = 0; i < 32; i += 2) + bytes.writeChar(i, i); + bytes.position(32); + for (int i = 32; i < 64; i += 2) + bytes.writeChar(i); + bytes.position(0); + for (int i = 0; i < 32; i += 2) + assertEquals(i, bytes.readChar()); + for (int i = 32; i < 64; i += 2) + assertEquals(i, bytes.readChar(i)); + } + + @Test + public void testReadWriteUnsignedShort() { + for (int i = 0; i < 32; i += 2) + bytes.writeUnsignedShort(i, ~i); + bytes.position(32); + for (int i = 32; i < 64; i += 2) + bytes.writeUnsignedShort(~i); + bytes.position(0); + for (int i = 0; i < 32; i += 2) + assertEquals(~i & 0xFFFF, bytes.readUnsignedShort()); + for (int i = 32; i < 64; i += 2) + assertEquals(~i & 0xFFFF, bytes.readUnsignedShort(i)); + } + + @Test + public void testReadWriteInt() { + for (int i = 0; i < 32; i += 4) + bytes.writeInt(i, i); + bytes.position(32); + for (int i = 32; i < 64; i += 4) + bytes.writeInt(i); + bytes.position(0); + for (int i = 0; i < 32; i += 4) + assertEquals(i, bytes.readInt()); + for (int i = 32; i < 64; i += 4) + assertEquals(i, bytes.readInt(i)); + } + + @Test + public void testReadWriteThreadeSafeInt() { + for (int i = 0; i < 32; i += 4) + bytes.writeOrderedInt(i, i); + bytes.position(32); + for (int i = 32; i < 64; i += 4) + bytes.writeOrderedInt(i); + bytes.position(0); + for (int i = 0; i < 32; i += 4) + assertEquals(i, bytes.readVolatileInt()); + for (int i = 32; i < 64; i += 4) + assertEquals(i, bytes.readVolatileInt(i)); + } + + @Test + public void testReadWriteFloat() { + for (int i = 0; i < 32; i += 4) + bytes.writeFloat(i, i); + bytes.position(32); + for (int i = 32; i < 64; i += 4) + bytes.writeFloat(i); + bytes.position(0); + for (int i = 0; i < 32; i += 4) + assertEquals(i, bytes.readFloat(), 0); + for (int i = 32; i < 64; i += 4) + assertEquals(i, bytes.readFloat(i), 0); + } + + @Test + public void testReadWriteUnsignedInt() { + for (int i = 0; i < 32; i += 4) + bytes.writeUnsignedInt(i, ~i); + bytes.position(32); + for (int i = 32; i < 64; i += 4) + bytes.writeUnsignedInt(~i); + bytes.position(0); + for (int i = 0; i < 32; i += 4) + assertEquals(~i & 0xFFFFFFFFL, bytes.readUnsignedInt()); + for (int i = 32; i < 64; i += 4) + assertEquals(~i & 0xFFFFFFFFL, bytes.readUnsignedInt(i)); + } + + @Test + public void testReadWriteInt24() { + for (int i = 0; i < 30; i += 3) + bytes.writeInt24(i, ~i & 0x7FFFFF); + bytes.position(30); + for (int i = 30; i < 63; i += 3) + bytes.writeInt24(~i & 0x7FFFFF); + assertEquals(63, bytes.position()); + bytes.position(0); + for (int i = 0; i < 30; i += 3) + assertEquals("i: " + i, ~i & 0x7FFFFFL, bytes.readInt24()); + for (int i = 30; i < 63; i += 3) + assertEquals("i: " + i, ~i & 0x7FFFFFL, bytes.readInt24(i)); + // now negative + bytes.position(0); + for (int i = 0; i < 30; i += 3) + bytes.writeInt24(i, ~i); + bytes.position(30); + for (int i = 30; i < 63; i += 3) + bytes.writeInt24(~i); + assertEquals(63, bytes.position()); + bytes.position(0); + for (int i = 0; i < 30; i += 3) + assertEquals("i: " + i, ~i << 8 >> 8, bytes.readInt24()); + for (int i = 30; i < 63; i += 3) + assertEquals("i: " + i, ~i << 8 >> 8, bytes.readInt24(i)); + } + + @Test + public void testReadWriteInt48() { + for (long i = 0; i < 30; i += 6) + bytes.writeInt48(i, ~i & 0x7FFFFFFFFFFFL); + bytes.position(30); + for (long i = 30; i < 60; i += 6) + bytes.writeInt48(~i & 0x7FFFFFFFFFFFL); + assertEquals(60, bytes.position()); + bytes.position(0); + for (long i = 0; i < 30; i += 6) + assertEquals("i: " + i, ~i & 0x7FFFFFFFFFFFL, bytes.readInt48()); + for (long i = 30; i < 60; i += 6) + assertEquals("i: " + i, ~i & 0x7FFFFFFFFFFFL, bytes.readInt48(i)); + // now negative + bytes.position(0); + for (long i = 0; i < 30; i += 6) + bytes.writeInt48(i, ~i); + bytes.position(30); + for (long i = 30; i < 60; i += 6) + bytes.writeInt48(~i); + assertEquals(60, bytes.position()); + bytes.position(0); + for (long i = 0; i < 30; i += 6) + assertEquals("i: " + i, ~i << 16 >> 16, bytes.readInt48()); + for (long i = 30; i < 60; i += 6) + assertEquals("i: " + i, ~i << 16 >> 16, bytes.readInt48(i)); + } + + @Test + public void testDateTimes() { + long now = System.currentTimeMillis(); + bytes.appendDateTimeMillis(now); + bytes.append(' '); + bytes.appendDateMillis(now); + bytes.append('T'); + bytes.appendTimeMillis(now % 86400000L); + assertEquals(23 * 2 + 1, bytes.position()); + bytes.position(0); + 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)); + } + + @Test + public void testReadWriteCompactUnsignedShort() { + for (int i = 0; i < 64; i += 4) + bytes.writeCompactUnsignedShort(i); + assertEquals(64 / 4, bytes.position()); + bytes.position(0); + for (int i = 0; i < 64; i += 4) + assertEquals(i, bytes.readByte(i / 4)); + for (int i = 0; i < 64; i += 4) + assertEquals(i, bytes.readCompactUnsignedShort()); + } + + @Test + public void testReadWriteCompactUnsignedInt() { + for (int i = 0; i < 64; i += 4) + bytes.writeCompactUnsignedInt(i); + assertEquals(64 / 2, bytes.position()); + bytes.position(0); + for (int i = 0; i < 64; i += 4) + assertEquals(i, bytes.readShort(i / 2)); + for (int i = 0; i < 64; i += 4) + assertEquals(i, bytes.readCompactUnsignedInt()); + } + + @Test + public void testReadWriteLong() { + for (long i = 0; i < 32; i += 8) + bytes.writeLong(i, i); + bytes.position(32); + for (long i = 32; i < 64; i += 8) + bytes.writeLong(i); + bytes.position(0); + for (long i = 0; i < 32; i += 8) + assertEquals(i, bytes.readLong()); + for (long i = 32; i < 64; i += 8) + assertEquals(i, bytes.readLong(i)); + } + + @Test + public void testReadWriteThreadSafeLong() { + for (long i = 0; i < 32; i += 8) + bytes.writeOrderedLong(i, i); + bytes.position(32); + for (long i = 32; i < 64; i += 8) + bytes.writeOrderedLong(i); + bytes.position(0); + for (long i = 0; i < 32; i += 8) + assertEquals(i, bytes.readVolatileLong()); + for (long i = 32; i < 64; i += 8) + assertEquals(i, bytes.readVolatileLong(i)); + } + + @Test + public void testReadWriteDouble() { + for (long i = 0; i < 32; i += 8) + bytes.writeDouble(i, i); + bytes.position(32); + for (long i = 32; i < 64; i += 8) + bytes.writeDouble(i); + bytes.position(0); + for (long i = 0; i < 32; i += 8) + assertEquals(i, bytes.readDouble(), 0); + for (long i = 32; i < 64; i += 8) + assertEquals(i, bytes.readDouble(i), 0); + } + + @Test + public void testAppendSubstring() { + bytes.append("Hello World", 2, 7).append("\n"); + bytes.position(0); + assertEquals("Hello World".substring(2, 7), bytes.parseUTF(CONTROL_STOP)); + } + + @Test + public void testWriteReadEnum() { + bytes.append(BuySell.Buy).append("\t").append(BuySell.Sell); + bytes.position(0); + assertEquals(BuySell.Buy, bytes.parseEnum(BuySell.class, CONTROL_STOP)); + assertEquals(BuySell.Sell, bytes.parseEnum(BuySell.class, CONTROL_STOP)); + assertEquals(null, bytes.parseEnum(BuySell.class, CONTROL_STOP)); + } + + @Test + public void testAppendParse() { + bytes.append(false).append(' '); + bytes.append(true).append(' '); + bytes.append("what?").append(' '); + bytes.append("word£€").append(' '); + bytes.append(BuySell.Buy).append(' '); + 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(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 testWriteByteChar() throws UnsupportedEncodingException { + bytes.writeBytes("Hello \u00ff\u01fe\u02fc\n"); + bytes.writeChars("Hello \u00ff\u01fe\u02fc\n"); + byte[] bytes = new byte[(int) this.bytes.position()]; + this.bytes.position(0); + this.bytes.readFully(bytes); + assertEquals("Hello \u00ff\u00fe\u00fc\n" + + "H\u0000e\u0000l\u0000l\u0000o\u0000 \u0000ÿ\u0000þ\u0001ü\u0002\n" + + "\u0000", new String(bytes, "ISO-8859-1")); + } + + @Test + 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.position(0); + assertEquals("Hell0 worl bye", bytes.parseUTF(CONTROL_STOP)); + } + + @Test + public void testAppendIterable() { + 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)); + } + + @Test + public void readWriteMutableDecimal() { + Random rand = new Random(2); + MutableDecimal md = new MutableDecimal(); + MutableDecimal md2 = new MutableDecimal(); +// md.set(1260042744, 0); + + for (int i = 0; i < 20000; i++) { + int n = rand.nextInt(); + for (int j = 0; j < 6; j++) { + testDecimal0(md, md2, n, j); + } + } + } + + private void testDecimal0(MutableDecimal md, MutableDecimal md2, int n, int j) { + md.set(n, j); + bytes.position(0); + bytes.append(md).append('\n'); + bytes.position(0); + bytes.parseDecimal(md2); + bytes.position(0); + String text = bytes.parseUTF(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)); + GZIPOutputStream out = new GZIPOutputStream(bytes.outputStream()); + out.write("Hello world\n".getBytes()); + out.close(); + bytes.position(0); + GZIPInputStream in = new GZIPInputStream(bytes.inputStream()); + byte[] bytes = new byte[12]; + for (int i = 0; i < 12; i++) + bytes[i] = (byte) in.read(); + assertEquals(-1, in.read()); + assertEquals("Hello world\n", new String(bytes)); + in.close(); + } + + @Test + public void testStream2() throws IOException { + OutputStream out = bytes.outputStream(); + out.write(11); + out.write(22); + out.write(33); + out.write(44); + out.write(55); + + bytes.position(0); + InputStream in = bytes.inputStream(); + assertTrue(in.markSupported()); + assertEquals(11, in.read()); + in.mark(1); + assertEquals(1, bytes.position()); + assertEquals(22, in.read()); + assertEquals(2, bytes.position()); + + assertEquals(33, in.read()); + in.reset(); + + assertEquals(1, bytes.position()); + assertEquals(22, in.read()); + + assertEquals(2, in.skip(2)); + assertEquals(4, bytes.position()); + assertEquals(SIZE - 4, bytes.available()); + assertEquals(55, in.read()); + in.close(); + } + + @Test + public void testWriteObject() { + for (Object o : new Object[]{10, 9.9, "string", new Date(), BigDecimal.valueOf(1.1)}) { + bytes.position(0); + bytes.writeObject(o); +// System.out.println(o +" size: "+bytes.position()); + assertTrue(bytes.position() < 21); + bytes.position(0); + Object o2 = bytes.readObject(); + bytes.position(0); + Object o3 = bytes.readObject(o.getClass()); + assertEquals(o, o2); + assertEquals(o, o3); + } + } + + @Test + public void testWriteSerializable() { + int capacity = 16 * 1024; + byteBuffer = ByteBuffer.allocateDirect(capacity); + bytes = new ByteBufferBytes(byteBuffer); + Calendar cal = Calendar.getInstance(); + bytes.writeObject(cal); + Dummy d = new Dummy(); + bytes.writeObject(d); + bytes.position(0); + Calendar cal2 = bytes.readObject(Calendar.class); + Dummy d2 = bytes.readObject(Dummy.class); + assertEquals(cal, cal2); + assertEquals(d, d2); + } + + @Test + public void testAddAndGet() { + for (int i = 0; i < 10; i++) + bytes.addAndGetInt(0L, 10); + assertEquals(100, bytes.readInt(0L)); + assertEquals(0, bytes.readInt(4L)); + + for (int i = 0; i < 11; i++) + bytes.getAndAdd(4L, 11); + assertEquals(100, bytes.readInt(0L)); + assertEquals(11 * 11, bytes.readInt(4L)); + } + + enum BuySell { + Buy, Sell + } + + static class Dummy implements Serializable { + @Override + public boolean equals(Object obj) { + return obj instanceof Dummy; + } + } + + @Test + public void testErrors() { + int capacity = 1024; + byteBuffer = ByteBuffer.allocateDirect(capacity); + // it is actually much bigger than it believes + bytes = new ByteBufferBytes(byteBuffer, 0, 16); + bytes.writeLong(8); + assertFalse(bytes.isFinished()); + bytes.finish(); + assertTrue(bytes.isFinished()); + bytes.flush(); + bytes.writeLong(16); + bytes.finish(); + bytes.flush(); + + try { + bytes.writeLong(24); + fail(); + } catch (IndexOutOfBoundsException expected) { + } + bytes.finish(); + bytes.flush(); + + bytes.reset(); + assertEquals(0, bytes.position()); + assertEquals(8, bytes.skip(8)); + assertEquals(8, bytes.position()); + bytes.writeLong(22); + bytes.close(); + } + + @Test + public void testWriteList() { + List<Integer> ints = Arrays.asList(1, 2, 3, 4); + bytes.writeList(ints); + bytes.reset(); + List<Integer> ints2 = new ArrayList<Integer>(); + bytes.readList(ints2, Integer.class); + assertEquals(ints, ints2); + + bytes.reset(); + List<String> words = Arrays.asList("Hello word byte for now".split(" ")); + bytes.writeList(words); + bytes.reset(); + List<String> words2 = new ArrayList<String>(); + bytes.readList(words2, String.class); + } + + @Test + public void testWriteMap() { + Map<String, Integer> map = new LinkedHashMap<String, Integer>() { + { + put("one", 1); + put("two", 2); + put("three", 3); + put("four", 4); + } + }; + + bytes.writeMap(map); + bytes.finish(); + + bytes.reset(); + Map<String, Integer> map2 = new LinkedHashMap<String, Integer>(); + bytes.readMap(map2, String.class, Integer.class); + assertEquals(map, map2); + } + + @Test + public void unloadFailed() throws InterruptedException { + bytes.busyLockInt(0); + ExecutorService es = Executors.newSingleThreadExecutor(new NamedThreadFactory("unloadFailed")); + Future<Void> future = es.submit(new Callable<Void>() { + @Override + public Void call() throws Exception { + bytes.unlockInt(0); + return null; + } + }); + es.shutdown(); + try { + future.get(); + fail(); + } catch (ExecutionException e) { + assertEquals(IllegalMonitorStateException.class, e.getCause().getClass()); + } + } +} diff --git a/lang/src/test/java/net/openhft/lang/io/DirectBytesTest.java b/lang/src/test/java/net/openhft/lang/io/DirectBytesTest.java new file mode 100755 index 0000000..090b0b5 --- /dev/null +++ b/lang/src/test/java/net/openhft/lang/io/DirectBytesTest.java @@ -0,0 +1,300 @@ +/* + * 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.io; + +import org.jetbrains.annotations.NotNull; +import org.junit.Test; + +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(); + + int records = 64; + for (int i = 0; i < lockCount; i += records) { + for (long j = 0; j < records * 64; j += 64) { + slice1.positionAndSize(j, 64); + boolean condition = false; + for (int k = 0; k < 20; k++) { + condition = slice1.tryLockInt(0L) || slice1.tryLockNanosInt(0L, 5 * 1000 * 1000); + if (condition) + break; + if (k > 0) + System.out.println("k: " + (5 + k * 5)); + } + if (!condition) + assertTrue("i= " + i, condition); + int toggle1 = slice1.readInt(4); + if (toggle1 == from) { + slice1.writeInt(4L, to); + } else { + // noinspection AssignmentToForLoopParameter,AssignmentToForLoopParameter + i--; + } + slice1.unlockInt(0L); + System.currentTimeMillis(); // small delay + } + } + } + + private static void manyLongToggles(@NotNull DirectStore store1, int lockCount, int from, int to) { + long id = Thread.currentThread().getId(); + assertEquals(0, id >>> 32); + System.out.println("Thread " + id); + + DirectBytes slice1 = store1.createSlice(); + + int records = 64; + for (int i = 0; i < lockCount; i += records) { + for (long j = 0; j < records * 64; j += 64) { + slice1.positionAndSize(j, 64); + boolean condition = false; + for (int k = 0; k < 20; k++) { + condition = slice1.tryLockLong(0L) || slice1.tryLockNanosLong(0L, 5 * 1000 * 1000); + if (condition) + break; + if (k > 0) + System.out.println("k: " + (5 + k * 5)); + } + if (!condition) + assertTrue("i= " + i, condition); + long toggle1 = slice1.readLong(8); + if (toggle1 == from) { + slice1.writeLong(8L, to); + } else { + // noinspection AssignmentToForLoopParameter,AssignmentToForLoopParameter + i--; + } + slice1.unlockLong(0L); + System.currentTimeMillis(); // small delay + } + } + } + + @Test + public void testAllocate() throws Exception { + long size = 1L << 24; // 31; don't overload cloud-bees + DirectStore store = DirectStore.allocate(size); + assertEquals(size, store.size()); + DirectBytes slice = store.createSlice(); + slice.positionAndSize(0, size); + slice.writeLong(0, size); + slice.writeLong(size - 8, size); + store.free(); + } + + @Test + public void testLocking() { + if (Runtime.getRuntime().availableProcessors() < 2) { + System.err.println("Test requires 2 CPUs, skipping"); + return; + } + long start = System.nanoTime(); + // a page + final DirectStore store1 = DirectStore.allocate(1 << 12); + final int lockCount = 10 * 1000 * 1000; + new Thread(new Runnable() { + @Override + public void run() { + manyToggles(store1, lockCount, 1, 0); + } + }).start(); + + manyToggles(store1, lockCount, 0, 1); + + store1.free(); + long time = System.nanoTime() - start; + System.out.printf("Contended lock rate was %,d per second%n", (int) (lockCount * 2 * 1e9 / time)); + } + + @Test + public void testLockingLong() { + if (Runtime.getRuntime().availableProcessors() < 2) { + System.err.println("Test requires 2 CPUs, skipping"); + return; + } + long start = System.nanoTime(); + // a page + final DirectStore store1 = DirectStore.allocate(1 << 12); + final int lockCount = 10 * 1000 * 1000; + new Thread(new Runnable() { + @Override + public void run() { + manyLongToggles(store1, lockCount, 1, 0); + } + }).start(); + + manyLongToggles(store1, lockCount, 0, 1); + + store1.free(); + long time = System.nanoTime() - start; + System.out.printf("Contended lock rate was %,d per second%n", (int) (lockCount * 2 * 1e9 / time)); + } + + @Test + public void testLocking2() throws Exception { + if (Runtime.getRuntime().availableProcessors() < 2) { + System.err.println("Test requires 2 CPUs, skipping"); + return; + } + // a page + final DirectStore store1 = DirectStore.allocate(1 << 12); + final DirectStore store2 = DirectStore.allocate(1 << 12); + final int lockCount = 1000000; + + Thread t = new Thread(new Runnable() { + @Override + public void run() { + long id = Thread.currentThread().getId(); + System.out.println("Thread " + id); + assertEquals(0, id >>> 24); + int expected = (1 << 24) | (int) id; + try { + DirectBytes slice1 = store1.createSlice(); + DirectBytes slice2 = store2.createSlice(); + + for (int i = 0; i < lockCount; i += 2) { + slice1.busyLockInt(0); + slice2.busyLockInt(0); + int lockValue1 = slice1.readInt(0); + if (lockValue1 != expected) + assertEquals(expected, lockValue1); + int lockValue2 = slice2.readInt(0); + if (lockValue2 != expected) + assertEquals(expected, lockValue2); + int toggle1 = slice1.readInt(4); + if (toggle1 == 1) { + slice1.writeInt(4, 0); + // if (i % 10000== 0) + // System.out.println("t: " + i); + } else { + // noinspection AssignmentToForLoopParameter + i--; + } + int toggle2 = slice2.readInt(4); + if (toggle2 == 1) { + slice2.writeInt(4, 0); + // if (i % 10000== 0) + // System.out.println("t: " + i); + } else { + // noinspection AssignmentToForLoopParameter + i--; + } + int lockValue1A = slice1.readInt(0); + int lockValue2A = slice1.readInt(0); + try { + slice2.unlockInt(0); + slice1.unlockInt(0); + } catch (IllegalStateException e) { + int lockValue1B = slice1.readInt(0); + int lockValue2B = slice2.readInt(0); + System.err.println("i= " + i + + " lock: " + Integer.toHexString(lockValue1A) + " / " + Integer.toHexString(lockValue2A) + + " lock: " + Integer.toHexString(lockValue1B) + " / " + Integer.toHexString(lockValue2B)); + throw e; + } + } + } catch (InterruptedException e) { + e.printStackTrace(); + } + } + }); + t.start(); + + long id = Thread.currentThread().getId(); + assertEquals(0, id >>> 24); + int expected = (1 << 24) | (int) id; + System.out.println("Thread " + id); + + DirectBytes slice1 = store1.createSlice(); + DirectBytes slice2 = store2.createSlice(); + for (int i = 0; i < lockCount; i += 2) { + slice1.busyLockInt(0); + slice2.busyLockInt(0); + int lockValue1 = slice1.readInt(0); + if (lockValue1 != expected) + assertEquals(expected, lockValue1); + int lockValue2 = slice2.readInt(0); + if (lockValue2 != expected) + assertEquals(expected, lockValue2); + int toggle1 = slice1.readInt(4); + if (toggle1 == 0) { + slice1.writeInt(4, 1); + // if (i % 10000== 0) + // System.out.println("t: " + i); + } else { + // noinspection AssignmentToForLoopParameter + i--; + } + int toggle2 = slice2.readInt(4); + if (toggle2 == 0) { + slice2.writeInt(4, 1); + // if (i % 10000== 0) + // System.out.println("t: " + i); + } else { + // noinspection AssignmentToForLoopParameter + i--; + } + int lockValue1A = slice1.readInt(0); + int lockValue2A = slice1.readInt(0); + try { + slice2.unlockInt(0); + slice1.unlockInt(0); + } catch (IllegalStateException e) { + int lockValue1B = slice1.readInt(0); + int lockValue2B = slice2.readInt(0); + System.err.println("i= " + i + + " lock: " + Integer.toHexString(lockValue1A) + " / " + Integer.toHexString(lockValue2A) + + " lock: " + Integer.toHexString(lockValue1B) + " / " + Integer.toHexString(lockValue2B)); + throw e; + } + } + + store1.free(); + store2.free(); + } + +} diff --git a/lang/src/test/java/net/openhft/lang/io/IOToolsTest.java b/lang/src/test/java/net/openhft/lang/io/IOToolsTest.java new file mode 100644 index 0000000..086749d --- /dev/null +++ b/lang/src/test/java/net/openhft/lang/io/IOToolsTest.java @@ -0,0 +1,47 @@ +/* + * 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.io; + +import org.junit.Test; + +import java.io.Closeable; +import java.io.IOException; +import java.util.Arrays; + +import static org.junit.Assert.assertEquals; + +/** + * User: peter.lawrey + * Date: 20/09/13 + * Time: 09:54 + */ +public class IOToolsTest { + @Test + public void testClose() { + final int[] count = {0}; + Closeable c = new Closeable() { + @Override + public void close() throws IOException { + count[0]++; + } + }; + IOTools.close(c); + assertEquals(1, count[0]); + IOTools.close(Arrays.asList(c, c, c)); + assertEquals(4, count[0]); + } +} diff --git a/lang/src/test/java/net/openhft/lang/io/LockingViaFileLockMain.java b/lang/src/test/java/net/openhft/lang/io/LockingViaFileLockMain.java new file mode 100644 index 0000000..46abc86 --- /dev/null +++ b/lang/src/test/java/net/openhft/lang/io/LockingViaFileLockMain.java @@ -0,0 +1,94 @@ +/* + * 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.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; + +/** + * User: peter + * Date: 22/12/13 + * Time: 11:05 + * + * Toggled 10,000,128 times with an average delay of 2,402 ns + */ +public class LockingViaFileLockMain { + 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); + + // offsets +// static int LOCK = 0; + static int FLAG = 8; + + public static void main(String... args) throws IOException, InterruptedException { + boolean toggleTo = Boolean.parseBoolean(args[0]); + 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())); + + long start = 0; + for (int i = -WARMUP / RECORDS; i < (RUNS + RECORDS - 1) / RECORDS; i++) { + if (i == 0) { + start = System.nanoTime(); + System.out.println("Started"); + } + + for (int j = 0; j < RECORDS; j++) { + int recordOffset = j * RECORD_SIZE; + for (int t = 9999; t >= 0; t--) { + if (t == 0) + if (i >= 0) { + throw new AssertionError("Didn't toggle in time !??"); + } else { + System.out.println("waiting"); + t = 99999; + Thread.sleep(200); + } + final FileLock lock = fc.lock(); + try { + bytes.readBarrier(); + boolean flag = bytes.readBoolean(recordOffset + FLAG); + if (flag == toggleTo) { + if (t % 100 == 0) + System.out.println("j: " + j + " is " + flag); + continue; + } + bytes.writeBoolean(recordOffset + FLAG, toggleTo); + bytes.writeBarrier(); + break; + } finally { + lock.release(); + } + } + } + } + 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); + fc.close(); + tmpFile.deleteOnExit(); + } +} diff --git a/lang/src/test/java/net/openhft/lang/io/LockingViaMMapMain.java b/lang/src/test/java/net/openhft/lang/io/LockingViaMMapMain.java new file mode 100644 index 0000000..a1cddd1 --- /dev/null +++ b/lang/src/test/java/net/openhft/lang/io/LockingViaMMapMain.java @@ -0,0 +1,97 @@ +/* + * 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.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; + +/** + * User: peter + * Date: 22/12/13 + * Time: 11:05 + * + * Toggled 10,000,128 times with an average delay of 28 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); + + // offsets + static int LOCK = 0; + static int FLAG = 8; + static int LENGTH = 16; + + public static void main(String... args) throws IOException, InterruptedException { + boolean toggleTo = Boolean.parseBoolean(args[0]); + 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(); + + long start = 0; + for (int i = -WARMUP / RECORDS; i < (RUNS + RECORDS - 1) / RECORDS; i++) { + if (i == 0) { + start = System.nanoTime(); + System.out.println("Started"); + } + + for (int j = 0; j < RECORDS; j++) { + int recordOffset = j * RECORD_SIZE; + for (int t = 1; t < 10000; t++) { + if (t == 0) + if (i >= 0) { + throw new AssertionError("Didn't toggle in time !??"); + } else { + Thread.sleep(200); + } + bytes.busyLockLong(recordOffset + LOCK); + try { + boolean flag = bytes.readBoolean(recordOffset + FLAG); + if (flag != toggleTo) { + bytes.writeBoolean(recordOffset + FLAG, toggleTo); + break; + } + } finally { + bytes.unlockLong(recordOffset + LOCK); + } + if (t % 100 == 0) + System.out.println("waiting for " + j + + " pid " + (int) bytes.readLong(recordOffset + LOCK) + + " is " + bytes.readBoolean(recordOffset + FLAG)); + if (t > 100) + if (t > 200) + Thread.sleep(1); + else + Thread.yield(); + } + } + } + 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); + 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 new file mode 100644 index 0000000..6456888 --- /dev/null +++ b/lang/src/test/java/net/openhft/lang/io/MappedFileTest.java @@ -0,0 +1,97 @@ +/* + * 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.io; + +import org.junit.Test; + +import java.io.*; +import java.nio.MappedByteBuffer; + +import static org.junit.Assert.assertTrue; + +public class MappedFileTest { + public static void printMappings() throws IOException { + BufferedReader br = new BufferedReader(new InputStreamReader(new FileInputStream("/proc/self/maps"))); + try { + for (String line; (line = br.readLine()) != null; ) { + System.out.println(line); + } + } catch (IOException ioe) { + br.close(); + throw ioe; + } + + } + + public static void delete(File file) throws IOException { + if (file.delete() || !file.exists()) return; + // get an error message as to why. + ProcessBuilder pb = new ProcessBuilder("/bin/rm", file.getAbsolutePath()); + pb.redirectErrorStream(true); + Process p = pb.start(); + StringWriter sw = new StringWriter(); + char[] chars = new char[512]; + Reader r = new InputStreamReader(p.getInputStream()); + for (int len; (len = r.read(chars)) > 0; ) + sw.write(chars, 0, len); + String msg = sw.toString().trim(); + if (msg.length() > 0) + throw new IOException(msg); + } + + @Test + public void testUnmap() throws IOException, InterruptedException { + + String TMP = System.getProperty("java.io.tmpdir"); + String basePath = TMP + "/testUnmap"; + File file = new File(basePath); + File dir = file.getParentFile(); + long free0 = dir.getFreeSpace(); + + MappedFile mfile = new MappedFile(basePath, 1024 * 1024); + MappedMemory map0 = mfile.acquire(0); + fill(map0.buffer()); + MappedMemory map1 = mfile.acquire(1); + fill(map1.buffer().force()); + long free1 = dir.getFreeSpace(); + + map1.release(); + map0.release(); + mfile.close(); + +// printMappings(); + long free2 = dir.getFreeSpace(); + delete(file); + long free3 = 0; + for (int i = 0; i < 100; i++) { + free3 = dir.getFreeSpace(); + System.out.println("Freed " + free0 + " ~ " + free1 + " ~ " + free2 + " ~ " + free3 + ", delete = " + file.delete()); + if (free3 > free1) + break; + Thread.sleep(500); + } + assertTrue("free3-free1: " + (free3 - free1), free3 > free1); + + + } + + private void fill(MappedByteBuffer buffer) { + buffer.position(0); + while (buffer.remaining() >= 8) + buffer.putLong(0x123456789ABCDEFL); + } +} diff --git a/lang/src/test/java/net/openhft/lang/io/MutableDecimalTest.java b/lang/src/test/java/net/openhft/lang/io/MutableDecimalTest.java new file mode 100644 index 0000000..ba6796e --- /dev/null +++ b/lang/src/test/java/net/openhft/lang/io/MutableDecimalTest.java @@ -0,0 +1,56 @@ +/* + * 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.io; + +import org.junit.Test; + +import static org.junit.Assert.*; + +/** + * User: peter.lawrey + * Date: 20/09/13 + * Time: 10:40 + */ +public class MutableDecimalTest { + @Test + public void testConstructor() { + MutableDecimal md = new MutableDecimal(Long.MAX_VALUE); + assertEquals(Long.MAX_VALUE, md.longValue()); + assertEquals("" + Long.MAX_VALUE, md.toString()); + + MutableDecimal md2 = new MutableDecimal(Long.MAX_VALUE, 10); + assertEquals(Long.MAX_VALUE / 1e10, md2.doubleValue(), 0); + assertEquals("922337203.6854775807", md2.toString()); + + MutableDecimal md3 = new MutableDecimal(Math.PI, 6); + assertEquals(3.141593, md3.doubleValue(), 0); + assertEquals(3.141593f, md3.floatValue(), 0); + assertEquals(3, md3.intValue()); + assertEquals("3.141593", md3.toString()); + + assertEquals(1, md.compareTo(md2)); + assertEquals(-1, md2.compareTo(md)); + assertEquals(1, md2.compareTo(md3)); + assertEquals(-1, md3.compareTo(md2)); + + assertEquals(3141593, md3.value()); + assertEquals(6, md3.scale()); + assertTrue(md3.isSet()); + md3.clear(); + assertFalse(md3.isSet()); + } +} diff --git a/lang/src/test/java/net/openhft/lang/io/NativeBytesTest.java b/lang/src/test/java/net/openhft/lang/io/NativeBytesTest.java new file mode 100644 index 0000000..2b50825 --- /dev/null +++ b/lang/src/test/java/net/openhft/lang/io/NativeBytesTest.java @@ -0,0 +1,926 @@ +/* + * 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.io; + +import net.openhft.lang.Maths; +import net.openhft.lang.thread.NamedThreadFactory; +import org.junit.Before; +import org.junit.Test; +import sun.nio.ch.DirectBuffer; + +import java.io.*; +import java.math.BigDecimal; +import java.nio.ByteBuffer; +import java.nio.ByteOrder; +import java.text.SimpleDateFormat; +import java.util.*; +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 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. + */ +public class NativeBytesTest { + public static final int SIZE = 128; + private NativeBytes bytes; + private ByteBuffer byteBuffer; + + @Before + public void beforeTest() { + byteBuffer = ByteBuffer.allocateDirect(SIZE); + long addr = ((DirectBuffer) byteBuffer).address(); + bytes = new NativeBytes(addr, addr, addr + SIZE); + } + + @Test + public void testLongHash() throws Exception { + byte[] bytes = {1, 2, 3, 4, 5, 6, 7, 8}; + long h = NativeBytes.longHash(bytes, 0, bytes.length); + assertFalse(h == 0); + byte[] bytes2 = {1, 2, 3, 4, 5, 6, 7, 8, 9}; + long h2 = NativeBytes.longHash(bytes2, 0, bytes2.length); + assertFalse(h2 == 0); + assertFalse(h2 == h); + } + + @Test + public void testRead() throws Exception { + for (int i = 0; i < bytes.capacity(); i++) + bytes.writeByte(i, i); + bytes.position(0); + for (int i = 0; i < bytes.capacity(); i++) + assertEquals((byte) i, bytes.read()); + for (int i = (int) (bytes.capacity() - 1); i >= 0; i--) { + assertEquals((byte) i, bytes.readByte(i)); + } + + } + + @Test + public void testReadFully() throws Exception { + for (int i = 0; i < bytes.capacity(); i++) + bytes.write(i); + bytes.position(0); + byte[] bytes = new byte[(int) this.bytes.capacity()]; + this.bytes.readFully(bytes); + for (int i = 0; i < this.bytes.capacity(); i++) + assertEquals((byte) i, bytes[i]); + } + + @Test + public void testCompareAndSetLong() throws Exception { + 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 { + for (int i = 0; i < bytes.capacity(); i++) + bytes.write(i); + for (int i = (int) (bytes.capacity() - 1); i >= 0; i--) { + bytes.position(i); + assertEquals((byte) i, bytes.read()); + } + } + + @Test + public void testCapacity() throws Exception { + assertEquals(SIZE, bytes.capacity()); + assertEquals(10, new NativeBytes(0, 0, 10).capacity()); + } + + @Test + public void testRemaining() throws Exception { + assertEquals(SIZE, bytes.remaining()); + bytes.position(10); + assertEquals(SIZE - 10, bytes.remaining()); + } + + @Test + public void testByteOrder() throws Exception { + assertEquals(ByteOrder.nativeOrder(), bytes.byteOrder()); + } + + @Test + public void testCheckEndOfBuffer() throws Exception { + bytes.checkEndOfBuffer(); + + bytes.position(SIZE + 2); + try { + bytes.checkEndOfBuffer(); + fail(); + } catch (IndexOutOfBoundsException expected) { + } + } + + @Test + public void testAppendDouble() { + testAppendDouble0(-6.895305375646115E24); + Random random = new Random(1); + for (int i = 0; i < 100000; i++) { + double d = Math.pow(1e32, random.nextDouble()) / 1e6; + if (i % 3 == 0) d = -d; + testAppendDouble0(d); + } + } + + private void testAppendDouble0(double d) { + bytes.position(0); + bytes.append(d).append(' '); + bytes.position(0); + double d2 = bytes.parseDouble(); + assertEquals(d, d2, 0); + } + + @Test + public void testAppendDouble2() { +// testAppendDouble0(-0.93879148954440506, 14); +// testAppendDouble0(-0.214980202661, 12); +// testAppendDouble0(-0.937082148896, 12); + testAppendDouble0(0.17805, 5); + Random random = new Random(1); + + for (int j = 0; j < 20000; j++) { + double d = random.nextDouble(); + if (j % 3 == 0) d = -d; +// if (j % 5 == 0) d *= 1e6; + for (int i = 0; i < 4; i++) { + testAppendDouble0(d, i); + } + } + } + + private void testAppendDouble0(double d, int precision) { + bytes.position(0); + bytes.append(d, precision).append(' '); + bytes.position(0); + String text = bytes.parseUTF(SPACE_STOP); + bytes.position(0); + assertEquals(0, bytes.position()); + double d2 = bytes.parseDouble(); + double d3 = (double) Math.round(d * Maths.power10(precision)) / Maths.power10(precision); +// if (precision >= 14) +// assertEquals("'" + text + "' p: " + precision + " v: " + new BigDecimal(d), d3, d2, 5e-29 * Maths.power10(precision)); +// else + assertEquals("'" + text + "' p: " + precision, d3, d2, 0); + } + + @Test + public void testWriteReadBytes() { + byte[] bytes = "Hello World!".getBytes(); + this.bytes.write(bytes); + byte[] bytes2 = new byte[bytes.length]; + this.bytes.position(0); + this.bytes.read(bytes2); + assertTrue(Arrays.equals(bytes, bytes2)); + + this.bytes.write(22, bytes); + byte[] bytes3 = new byte[bytes.length]; + this.bytes.skipBytes((int) (22 - this.bytes.position())); + assertEquals(bytes3.length, this.bytes.read(bytes3)); + assertTrue(Arrays.equals(bytes, bytes3)); + this.bytes.position(this.bytes.capacity()); + assertEquals(-1, this.bytes.read(bytes3)); + } + + @Test + public void testWriteReadUTFΔ() { + String[] words = "Hello,World!,Bye£€!".split(","); + for (String word : words) { + bytes.writeUTFΔ(word); + } + bytes.writeUTFΔ(null); + bytes.position(0); + for (String word : words) { + assertEquals(word, bytes.readUTFΔ()); + } + assertEquals(null, bytes.readUTFΔ()); + assertEquals("", bytes.readUTFΔ()); + assertEquals(26, bytes.position()); // check the size + + bytes.position(0); + StringBuilder sb = new StringBuilder(); + for (String word : words) { + assertTrue(bytes.readUTFΔ(sb)); + assertEquals(word, sb.toString()); + } + assertFalse(bytes.readUTFΔ(sb)); + assertTrue(bytes.readUTFΔ(sb)); + assertEquals("", sb.toString()); + } + + @Test + public void testWriteReadUTF() { + String[] words = "Hello,World!,Bye£€!".split(","); + for (String word : words) { + bytes.writeUTF(word); + } + bytes.writeUTF(""); + assertEquals(28, bytes.position()); // check the size, more bytes for less strings than writeUTFΔ + bytes.position(0); + for (String word : words) { + assertEquals(word, bytes.readUTF()); + } + assertEquals("", bytes.readUTF()); + } + + @Test + public void testAppendParseUTF() { + String[] words = "Hello,World!,Bye£€!".split(","); + for (String word : words) { + bytes.append(word).append('\t'); + } + bytes.append('\t'); + bytes.position(0); + for (String word : words) { + assertEquals(word, bytes.parseUTF(CONTROL_STOP)); + } + assertEquals("", bytes.parseUTF(CONTROL_STOP)); + + bytes.position(0); + StringBuilder sb = new StringBuilder(); + for (String word : words) { + bytes.parseUTF(sb, CONTROL_STOP); + assertEquals(word, sb.toString()); + } + bytes.parseUTF(sb, CONTROL_STOP); + assertEquals("", sb.toString()); + + bytes.position(0); + bytes.skipTo(CONTROL_STOP); + 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()); + + bytes.position(0); + bytes.stepBackAndSkipTo(CONTROL_STOP); + assertEquals(6, bytes.position()); + bytes.stepBackAndSkipTo(CONTROL_STOP); + assertEquals(6, bytes.position()); + bytes.position(10); + bytes.stepBackAndSkipTo(CONTROL_STOP); + assertEquals(13, bytes.position()); + + } + + @Test + public void testWriteReadLines() { + byte[] bytes = "Hello\nWorld!\r\nBye".getBytes(); + this.bytes.write(bytes); + this.bytes.position(0); + assertEquals("Hello", this.bytes.readLine()); + assertEquals("World!", this.bytes.readLine()); + assertTrue(this.bytes.readLine().startsWith("Bye")); + } + + @Test + public void testWriteReadByteBuffer() { + byte[] bytes = "Hello\nWorld!\r\nBye".getBytes(); + this.bytes.write(ByteBuffer.wrap(bytes)); + this.bytes.position(0); + byte[] bytes2 = new byte[bytes.length + 1]; + ByteBuffer bb2 = ByteBuffer.wrap(bytes2); + this.bytes.read(bb2); + + assertEquals(bytes2.length, bb2.position()); + assertTrue(Arrays.equals(bytes, Arrays.copyOf(bytes2, bytes.length))); + } + + @Test + public void testReadWriteBoolean() { + for (int i = 0; i < 32; i++) + bytes.writeBoolean(i, (i & 3) == 0); + bytes.position(32); + for (int i = 32; i < 64; i++) + bytes.writeBoolean((i & 5) == 0); + bytes.position(0); + for (int i = 0; i < 32; i++) + assertEquals((i & 3) == 0, bytes.readBoolean()); + for (int i = 32; i < 64; i++) + assertEquals((i & 5) == 0, bytes.readBoolean(i)); + } + + @Test + public void testReadWriteShort() { + for (int i = 0; i < 32; i += 2) + bytes.writeShort(i, i); + bytes.position(32); + for (int i = 32; i < 64; i += 2) + bytes.writeShort(i); + bytes.position(0); + for (int i = 0; i < 32; i += 2) + assertEquals(i, bytes.readShort()); + for (int i = 32; i < 64; i += 2) + assertEquals(i, bytes.readShort(i)); + } + + @Test + public void testReadWriteCompactShort() { + int[] ints = {Short.MIN_VALUE, Short.MAX_VALUE, -125, 0, 127, -10000, 10000}; + for (int i : ints) { + bytes.writeCompactShort(i); +// System.out.println(i + " " + bytes.position()); + } + assertEquals(5 + 2 * 3, bytes.position()); + + bytes.position(0); + for (int i : ints) + assertEquals(i, bytes.readCompactShort()); + } + + @Test + public void testReadWriteCompactInt() { + int[] ints = {-10000000, Integer.MIN_VALUE, Integer.MAX_VALUE, Short.MIN_VALUE + 3, 0, Short.MAX_VALUE, 10000000}; + for (int i : ints) { + bytes.writeCompactInt(i); +// System.out.println(i + " " + bytes.position()); + } + assertEquals(5 * 2 + 2 * 6, bytes.position()); + + bytes.position(0); + for (int i : ints) + assertEquals(i, bytes.readCompactInt()); + } + + @Test + public void testReadWriteCompactLong() { + long[] ints = {Long.MAX_VALUE, -100000000000L, Long.MIN_VALUE, Integer.MIN_VALUE + 3, 0, Integer.MAX_VALUE, 100000000000L}; + for (long i : ints) { + bytes.writeCompactLong(i); +// System.out.println(i + " " + bytes.position()); + } + assertEquals(5 * 4 + 2 * 12, bytes.position()); + + bytes.position(0); + for (long i : ints) + assertEquals(i, bytes.readCompactLong()); + } + + @Test + public void testReadWriteCompactDouble() { + double[] doubles = {1, 1000, 1000000, -100000000, 0.1f, 0.1, 0.5, 0.51}; + for (double i : doubles) { + bytes.writeCompactDouble(i); +// System.out.println(i + " " + bytes.position()); + } + assertEquals(6 * 4 + 2 * 12, bytes.position()); + + bytes.position(0); + for (double i : doubles) + assertEquals(i, bytes.readCompactDouble(), 0.0); + } + + @Test + public void testReadWriteStop() { + long[] longs = {Long.MIN_VALUE, Long.MAX_VALUE, Integer.MIN_VALUE, Integer.MAX_VALUE}; + for (long i : longs) { + bytes.writeStopBit(i); +// System.out.println(i + " " + bytes.position()); + } + assertEquals(9 + 10, +5 + 6, bytes.position()); + + bytes.position(0); + for (long i : longs) + assertEquals(i, bytes.readStopBit()); + } + + @Test + public void testReadWriteChar() { + for (int i = 0; i < 32; i += 2) + bytes.writeChar(i, i); + bytes.position(32); + for (int i = 32; i < 64; i += 2) + bytes.writeChar(i); + bytes.position(0); + for (int i = 0; i < 32; i += 2) + assertEquals(i, bytes.readChar()); + for (int i = 32; i < 64; i += 2) + assertEquals(i, bytes.readChar(i)); + } + + @Test + public void testReadWriteUnsignedShort() { + for (int i = 0; i < 32; i += 2) + bytes.writeUnsignedShort(i, ~i); + bytes.position(32); + for (int i = 32; i < 64; i += 2) + bytes.writeUnsignedShort(~i); + bytes.position(0); + for (int i = 0; i < 32; i += 2) + assertEquals(~i & 0xFFFF, bytes.readUnsignedShort()); + for (int i = 32; i < 64; i += 2) + assertEquals(~i & 0xFFFF, bytes.readUnsignedShort(i)); + } + + @Test + public void testReadWriteInt() { + for (int i = 0; i < 32; i += 4) + bytes.writeInt(i, i); + bytes.position(32); + for (int i = 32; i < 64; i += 4) + bytes.writeInt(i); + bytes.position(0); + for (int i = 0; i < 32; i += 4) + assertEquals(i, bytes.readInt()); + for (int i = 32; i < 64; i += 4) + assertEquals(i, bytes.readInt(i)); + } + + @Test + public void testReadWriteThreadeSafeInt() { + for (int i = 0; i < 32; i += 4) + bytes.writeOrderedInt(i, i); + bytes.position(32); + for (int i = 32; i < 64; i += 4) + bytes.writeOrderedInt(i); + bytes.position(0); + for (int i = 0; i < 32; i += 4) + assertEquals(i, bytes.readVolatileInt()); + for (int i = 32; i < 64; i += 4) + assertEquals(i, bytes.readVolatileInt(i)); + } + + @Test + public void testReadWriteFloat() { + for (int i = 0; i < 32; i += 4) + bytes.writeFloat(i, i); + bytes.position(32); + for (int i = 32; i < 64; i += 4) + bytes.writeFloat(i); + bytes.position(0); + for (int i = 0; i < 32; i += 4) + assertEquals(i, bytes.readFloat(), 0); + for (int i = 32; i < 64; i += 4) + assertEquals(i, bytes.readFloat(i), 0); + } + + @Test + public void testReadWriteUnsignedInt() { + for (int i = 0; i < 32; i += 4) + bytes.writeUnsignedInt(i, ~i); + bytes.position(32); + for (int i = 32; i < 64; i += 4) + bytes.writeUnsignedInt(~i); + bytes.position(0); + for (int i = 0; i < 32; i += 4) + assertEquals(~i & 0xFFFFFFFFL, bytes.readUnsignedInt()); + for (int i = 32; i < 64; i += 4) + assertEquals(~i & 0xFFFFFFFFL, bytes.readUnsignedInt(i)); + } + + @Test + public void testReadWriteInt24() { + for (int i = 0; i < 30; i += 3) + bytes.writeInt24(i, ~i & 0x7FFFFF); + bytes.position(30); + for (int i = 30; i < 63; i += 3) + bytes.writeInt24(~i & 0x7FFFFF); + assertEquals(63, bytes.position()); + bytes.position(0); + for (int i = 0; i < 30; i += 3) + assertEquals("i: " + i, ~i & 0x7FFFFFL, bytes.readInt24()); + for (int i = 30; i < 63; i += 3) + assertEquals("i: " + i, ~i & 0x7FFFFFL, bytes.readInt24(i)); + // now negative + bytes.position(0); + for (int i = 0; i < 30; i += 3) + bytes.writeInt24(i, ~i); + bytes.position(30); + for (int i = 30; i < 63; i += 3) + bytes.writeInt24(~i); + assertEquals(63, bytes.position()); + bytes.position(0); + for (int i = 0; i < 30; i += 3) + assertEquals("i: " + i, ~i << 8 >> 8, bytes.readInt24()); + for (int i = 30; i < 63; i += 3) + assertEquals("i: " + i, ~i << 8 >> 8, bytes.readInt24(i)); + } + + @Test + public void testReadWriteInt48() { + for (long i = 0; i < 30; i += 6) + bytes.writeInt48(i, ~i & 0x7FFFFFFFFFFFL); + bytes.position(30); + for (long i = 30; i < 60; i += 6) + bytes.writeInt48(~i & 0x7FFFFFFFFFFFL); + assertEquals(60, bytes.position()); + bytes.position(0); + for (long i = 0; i < 30; i += 6) + assertEquals("i: " + i, ~i & 0x7FFFFFFFFFFFL, bytes.readInt48()); + for (long i = 30; i < 60; i += 6) + assertEquals("i: " + i, ~i & 0x7FFFFFFFFFFFL, bytes.readInt48(i)); + // now negative + bytes.position(0); + for (long i = 0; i < 30; i += 6) + bytes.writeInt48(i, ~i); + bytes.position(30); + for (long i = 30; i < 60; i += 6) + bytes.writeInt48(~i); + assertEquals(60, bytes.position()); + bytes.position(0); + for (long i = 0; i < 30; i += 6) + assertEquals("i: " + i, ~i << 16 >> 16, bytes.readInt48()); + for (long i = 30; i < 60; i += 6) + assertEquals("i: " + i, ~i << 16 >> 16, bytes.readInt48(i)); + } + + @Test + public void testDateTimes() { + long now = System.currentTimeMillis(); + bytes.appendDateTimeMillis(now); + bytes.append(' '); + bytes.appendDateMillis(now); + bytes.append('T'); + bytes.appendTimeMillis(now % 86400000L); + assertEquals(23 * 2 + 1, bytes.position()); + bytes.position(0); + 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)); + } + + @Test + public void testReadWriteCompactUnsignedShort() { + for (int i = 0; i < 64; i += 4) + bytes.writeCompactUnsignedShort(i); + assertEquals(64 / 4, bytes.position()); + bytes.position(0); + for (int i = 0; i < 64; i += 4) + assertEquals(i, bytes.readByte(i / 4)); + for (int i = 0; i < 64; i += 4) + assertEquals(i, bytes.readCompactUnsignedShort()); + } + + @Test + public void testReadWriteCompactUnsignedInt() { + for (int i = 0; i < 64; i += 4) + bytes.writeCompactUnsignedInt(i); + assertEquals(64 / 2, bytes.position()); + bytes.position(0); + for (int i = 0; i < 64; i += 4) + assertEquals(i, bytes.readShort(i / 2)); + for (int i = 0; i < 64; i += 4) + assertEquals(i, bytes.readCompactUnsignedInt()); + } + + @Test + public void testReadWriteLong() { + for (long i = 0; i < 32; i += 8) + bytes.writeLong(i, i); + bytes.position(32); + for (long i = 32; i < 64; i += 8) + bytes.writeLong(i); + bytes.position(0); + for (long i = 0; i < 32; i += 8) + assertEquals(i, bytes.readLong()); + for (long i = 32; i < 64; i += 8) + assertEquals(i, bytes.readLong(i)); + } + + @Test + public void testReadWriteThreadSafeLong() { + for (long i = 0; i < 32; i += 8) + bytes.writeOrderedLong(i, i); + bytes.position(32); + for (long i = 32; i < 64; i += 8) + bytes.writeOrderedLong(i); + bytes.position(0); + for (long i = 0; i < 32; i += 8) + assertEquals(i, bytes.readVolatileLong()); + for (long i = 32; i < 64; i += 8) + assertEquals(i, bytes.readVolatileLong(i)); + } + + @Test + public void testReadWriteDouble() { + for (long i = 0; i < 32; i += 8) + bytes.writeDouble(i, i); + bytes.position(32); + for (long i = 32; i < 64; i += 8) + bytes.writeDouble(i); + bytes.position(0); + for (long i = 0; i < 32; i += 8) + assertEquals(i, bytes.readDouble(), 0); + for (long i = 32; i < 64; i += 8) + assertEquals(i, bytes.readDouble(i), 0); + } + + @Test + public void testAppendSubstring() { + bytes.append("Hello World", 2, 7).append("\n"); + bytes.position(0); + assertEquals("Hello World".substring(2, 7), bytes.parseUTF(CONTROL_STOP)); + } + + @Test + public void testWriteReadEnum() { + bytes.append(BuySell.Buy).append("\t").append(BuySell.Sell); + bytes.position(0); + assertEquals(BuySell.Buy, bytes.parseEnum(BuySell.class, CONTROL_STOP)); + assertEquals(BuySell.Sell, bytes.parseEnum(BuySell.class, CONTROL_STOP)); + assertEquals(null, bytes.parseEnum(BuySell.class, CONTROL_STOP)); + } + + @Test + public void testAppendParse() { + bytes.append(false).append(' '); + bytes.append(true).append(' '); + bytes.append("what?").append(' '); + bytes.append("word£€").append(' '); + bytes.append(BuySell.Buy).append(' '); + 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(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 testWriteByteChar() throws UnsupportedEncodingException { + bytes.writeBytes("Hello \u00ff\u01fe\u02fc\n"); + bytes.writeChars("Hello \u00ff\u01fe\u02fc\n"); + byte[] bytes = new byte[(int) this.bytes.position()]; + this.bytes.position(0); + this.bytes.readFully(bytes); + assertEquals("Hello \u00ff\u00fe\u00fc\n" + + "H\u0000e\u0000l\u0000l\u0000o\u0000 \u0000ÿ\u0000þ\u0001ü\u0002\n" + + "\u0000", new String(bytes, "ISO-8859-1")); + } + + @Test + 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.position(0); + assertEquals("Hell0 worl bye", bytes.parseUTF(CONTROL_STOP)); + } + + @Test + public void testAppendIterable() { + 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)); + } + + @Test + public void readWriteMutableDecimal() { + Random rand = new Random(2); + MutableDecimal md = new MutableDecimal(); + MutableDecimal md2 = new MutableDecimal(); +// md.set(1260042744, 0); + + for (int i = 0; i < 20000; i++) { + int n = rand.nextInt(); + for (int j = 0; j < 6; j++) { + testDecimal0(md, md2, n, j); + } + } + } + + private void testDecimal0(MutableDecimal md, MutableDecimal md2, int n, int j) { + md.set(n, j); + bytes.position(0); + bytes.append(md).append('\n'); + bytes.position(0); + bytes.parseDecimal(md2); + bytes.position(0); + String text = bytes.parseUTF(CONTROL_STOP); + if (!md.equals(md2)) + assertEquals("n: " + n + ", s: " + j + " t: " + text, md, md2); + } + + @Test + public void testStream() throws IOException { + GZIPOutputStream out = new GZIPOutputStream(bytes.outputStream()); + out.write("Hello world\n".getBytes()); + out.close(); + bytes.position(0); + GZIPInputStream in = new GZIPInputStream(bytes.inputStream()); + byte[] bytes = new byte[12]; + for (int i = 0; i < 12; i++) + bytes[i] = (byte) in.read(); + assertEquals(-1, in.read()); + assertEquals("Hello world\n", new String(bytes)); + in.close(); + } + + @Test + public void testStream2() throws IOException { + OutputStream out = bytes.outputStream(); + out.write(11); + out.write(22); + out.write(33); + out.write(44); + out.write(55); + + bytes.position(0); + InputStream in = bytes.inputStream(); + assertTrue(in.markSupported()); + assertEquals(11, in.read()); + in.mark(1); + assertEquals(1, bytes.position()); + assertEquals(22, in.read()); + assertEquals(2, bytes.position()); + + assertEquals(33, in.read()); + in.reset(); + + assertEquals(1, bytes.position()); + assertEquals(22, in.read()); + + assertEquals(2, in.skip(2)); + assertEquals(4, bytes.position()); + assertEquals(SIZE - 4, bytes.available()); + assertEquals(55, in.read()); + in.close(); + } + + @Test + public void testWriteObject() { + for (Object o : new Object[]{10, 9.9, "string", new Date(), BigDecimal.valueOf(1.1)}) { + bytes.position(0); + bytes.writeObject(o); +// System.out.println(o +" size: "+bytes.position()); + assertTrue(bytes.position() < 21); + bytes.position(0); + Object o2 = bytes.readObject(); + bytes.position(0); + Object o3 = bytes.readObject(o.getClass()); + assertEquals(o, o2); + assertEquals(o, o3); + } + } + + @Test + public void testWriteSerializable() { + int capacity = 16 * 1024; + byteBuffer = ByteBuffer.allocateDirect(capacity); + long addr = ((DirectBuffer) byteBuffer).address(); + bytes = new NativeBytes(addr, addr, addr + capacity); + Calendar cal = Calendar.getInstance(); + bytes.writeObject(cal); + Dummy d = new Dummy(); + bytes.writeObject(d); + bytes.position(0); + Calendar cal2 = bytes.readObject(Calendar.class); + Dummy d2 = bytes.readObject(Dummy.class); + assertEquals(cal, cal2); + assertEquals(d, d2); + } + + @Test + public void testAddAndGet() { + for (int i = 0; i < 10; i++) + bytes.addAndGetInt(0L, 10); + assertEquals(100, bytes.readInt(0L)); + assertEquals(0, bytes.readInt(4L)); + + for (int i = 0; i < 11; i++) + bytes.getAndAdd(4L, 11); + assertEquals(100, bytes.readInt(0L)); + assertEquals(11 * 11, bytes.readInt(4L)); + } + + enum BuySell { + Buy, Sell + } + + static class Dummy implements Serializable { + @Override + public boolean equals(Object obj) { + return obj instanceof Dummy; + } + } + + @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.writeLong(8); + assertFalse(bytes.isFinished()); + bytes.finish(); + assertTrue(bytes.isFinished()); + bytes.flush(); + bytes.writeLong(16); + bytes.finish(); + bytes.flush(); + bytes.writeLong(24); + try { + bytes.finish(); + fail(); + } catch (IndexOutOfBoundsException expected) { + } + try { + bytes.flush(); + fail(); + } catch (IndexOutOfBoundsException expected) { + } + bytes.reset(); + assertEquals(0, bytes.position()); + assertEquals(8, bytes.skip(8)); + assertEquals(8, bytes.position()); + bytes.writeLong(22); + bytes.close(); + } + + @Test + public void testWriteList() { + List<Integer> ints = Arrays.asList(1, 2, 3, 4); + bytes.writeList(ints); + bytes.reset(); + List<Integer> ints2 = new ArrayList<Integer>(); + bytes.readList(ints2, Integer.class); + assertEquals(ints, ints2); + + bytes.reset(); + List<String> words = Arrays.asList("Hello word byte for now".split(" ")); + bytes.writeList(words); + bytes.reset(); + List<String> words2 = new ArrayList<String>(); + bytes.readList(words2, String.class); + } + + @Test + public void testWriteMap() { + Map<String, Integer> map = new LinkedHashMap<String, Integer>() { + { + put("one", 1); + put("two", 2); + put("three", 3); + put("four", 4); + } + }; + + bytes.writeMap(map); + bytes.finish(); + + bytes.reset(); + Map<String, Integer> map2 = new LinkedHashMap<String, Integer>(); + bytes.readMap(map2, String.class, Integer.class); + assertEquals(map, map2); + } + + @Test + public void unloadFailed() throws InterruptedException { + bytes.busyLockInt(0); + ExecutorService es = Executors.newSingleThreadExecutor(new NamedThreadFactory("unloadFailed")); + Future<Void> future = es.submit(new Callable<Void>() { + @Override + public Void call() throws Exception { + bytes.unlockInt(0); + return null; + } + }); + es.shutdown(); + try { + future.get(); + fail(); + } catch (ExecutionException e) { + assertEquals(IllegalMonitorStateException.class, e.getCause().getClass()); + } + } +} 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 new file mode 100644 index 0000000..c9ec1b5 --- /dev/null +++ b/lang/src/test/java/net/openhft/lang/io/examples/ParserExampleMain.java @@ -0,0 +1,61 @@ +/* + * 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.io.examples; + +import net.openhft.lang.io.ByteBufferBytes; +import net.openhft.lang.io.StopCharTesters; + +import java.nio.ByteBuffer; + +import static org.junit.Assert.assertEquals; + +/** + * Run with -verbosegc -Xmx32m + * <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); + 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(); + // read the next message. + bufferBytes.write(bytes); + bufferBytes.position(0); + // decode message + String word = bufferBytes.parseUTF(StopCharTesters.COMMA_STOP); + double low = bufferBytes.parseDouble(); + double curr = bufferBytes.parseDouble(); + double high = bufferBytes.parseDouble(); + long sequence = bufferBytes.parseLong(); + if (i == 0) { + assertEquals("BAC", word); + assertEquals(12.32, low, 0.0); + assertEquals(12.54, curr, 0.0); + assertEquals(12.56, high, 0.0); + assertEquals(232443, sequence); + } + } + long time = System.nanoTime() - start; + System.out.println("Average time was " + time / runs + " nano-seconds"); + } +} 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 new file mode 100644 index 0000000..95df903 --- /dev/null +++ b/lang/src/test/java/net/openhft/lang/io/serialization/ByteMarshallableMarshallerTest.java @@ -0,0 +1,79 @@ +/* + * 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.io.serialization; + +import net.openhft.lang.io.Bytes; +import net.openhft.lang.io.NativeBytes; +import org.jetbrains.annotations.NotNull; +import org.junit.Test; +import sun.nio.ch.DirectBuffer; + +import java.nio.ByteBuffer; + +import static org.junit.Assert.assertEquals; + +/** + * User: peter.lawrey + * Date: 20/09/13 + * Time: 09:28 + */ +public class ByteMarshallableMarshallerTest { + @Test + public void testMarshallable() { + int capacity = 2 * 1024; + ByteBuffer byteBuffer = ByteBuffer.allocateDirect(capacity); + long addr = ((DirectBuffer) byteBuffer).address(); + NativeBytes nativeBytes = new NativeBytes(addr, addr, addr + capacity); + + BytesMarshallable bm = new MockBytesMarshallable(12345678); + nativeBytes.writeObject(bm); + nativeBytes.finish(); + nativeBytes.reset(); + BytesMarshallable bm2 = nativeBytes.readObject(MockBytesMarshallable.class); + assertEquals(bm, bm2); + } + + static class MockBytesMarshallable implements BytesMarshallable { + long number; + + MockBytesMarshallable(long number) { + this.number = number; + } + + @Override + public void readMarshallable(@NotNull Bytes in) throws IllegalStateException { + number = in.readLong(); + } + + @Override + public void writeMarshallable(@NotNull Bytes out) { + out.writeLong(number); + } + + @Override + public boolean equals(Object o) { + if (this == o) return true; + if (o == null || getClass() != o.getClass()) return false; + + MockBytesMarshallable that = (MockBytesMarshallable) o; + + if (number != that.number) return false; + + 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 new file mode 100644 index 0000000..70bad3e --- /dev/null +++ b/lang/src/test/java/net/openhft/lang/io/serialization/ExternalizableMarshallerTest.java @@ -0,0 +1,81 @@ +/* + * 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.io.serialization; + +import net.openhft.lang.io.NativeBytes; +import org.junit.Test; +import sun.nio.ch.DirectBuffer; + +import java.io.Externalizable; +import java.io.IOException; +import java.io.ObjectInput; +import java.io.ObjectOutput; +import java.nio.ByteBuffer; + +import static org.junit.Assert.assertEquals; + +/** + * User: peter.lawrey + * Date: 20/09/13 + * Time: 09:28 + */ +public class ExternalizableMarshallerTest { + @Test + public void testMarshallable() { + int capacity = 2 * 1024; + ByteBuffer byteBuffer = ByteBuffer.allocateDirect(capacity); + long addr = ((DirectBuffer) byteBuffer).address(); + NativeBytes nativeBytes = new NativeBytes(addr, addr, addr + capacity); + + Externalizable bm = new MockExternalizable(12345678); + nativeBytes.writeObject(bm); + nativeBytes.finish(); + nativeBytes.reset(); + Externalizable bm2 = nativeBytes.readObject(MockExternalizable.class); + assertEquals(bm, bm2); + } + + static class MockExternalizable implements Externalizable { + long number; + + MockExternalizable(long number) { + this.number = number; + } + + @Override + public void writeExternal(ObjectOutput out) throws IOException { + out.writeLong(number); + } + + @Override + public void readExternal(ObjectInput in) throws IOException, ClassNotFoundException { + number = in.readLong(); + } + + @Override + public boolean equals(Object o) { + if (this == o) return true; + if (o == null || getClass() != o.getClass()) return false; + + MockExternalizable that = (MockExternalizable) o; + + if (number != that.number) return false; + + return true; + } + } +} diff --git a/lang/src/test/java/net/openhft/lang/io/serialization/RawCopierTest.java b/lang/src/test/java/net/openhft/lang/io/serialization/RawCopierTest.java new file mode 100644 index 0000000..1b89806 --- /dev/null +++ b/lang/src/test/java/net/openhft/lang/io/serialization/RawCopierTest.java @@ -0,0 +1,93 @@ +/* + * 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.io.serialization; + +import net.openhft.lang.io.DirectBytes; +import net.openhft.lang.io.DirectStore; +import net.openhft.lang.io.NativeBytes; +import org.junit.Test; + +import java.awt.geom.Point2D; +import java.util.Map; + +import static org.junit.Assert.assertEquals; + +/** + * User: peter.lawrey Date: 22/09/13 Time: 17:06 + */ +public class RawCopierTest { + static void printInts(Object o, int len) { + for (long i = 0; i < len; i += 4) { + System.out.print(NativeBytes.UNSAFE.getInt(o, i) + " "); + } + System.out.println(); + } + + @Test + public void testStartEnd() { + RawCopier<A> aRawCopier = RawCopier.copies(A.class); + if (aRawCopier.start != 8) + assertEquals(12, aRawCopier.start); + assertEquals(aRawCopier.start + 3 * 4, aRawCopier.end); + + RawCopier<B> bRawCopier = RawCopier.copies(B.class); + if (aRawCopier.start != 8) + assertEquals(16, bRawCopier.start); + assertEquals(bRawCopier.start + 4 * 8, bRawCopier.end); + } + + @Test + public void testReadWrite() { + DirectStore ds = new DirectStore(null, 1024); + DirectBytes db = ds.createSlice(); + RawCopier<A> aRawCopier = RawCopier.copies(A.class); + A a = new A(); + a.i = 111; + a.j = -222; + a.k = 333; + a.s = "Hello"; + aRawCopier.toBytes(a, db); + assertEquals(12, db.position()); + + assertEquals(111, db.readInt(0)); + assertEquals(-222, db.readInt(4)); + assertEquals(333, db.readInt(8)); + + A a2 = new A(); + a2.i = 1; + a2.j = 2; + a2.k = 3; +// printInts(a2, 28); + db.position(0); + aRawCopier.fromBytes(db, a2); +// printInts(a2, 28); + assertEquals(111, a2.i); + assertEquals(-222, a2.j); + assertEquals(333, a2.k); + assertEquals(null, a2.s); + } + + static class A { + int i, j, k; + String s; + transient Map map; + } + + static class B extends Point2D.Double { + double z, w; + } +} 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 new file mode 100644 index 0000000..b258e32 --- /dev/null +++ b/lang/src/test/java/net/openhft/lang/io/serialization/VanillaBytesMarshallerTest.java @@ -0,0 +1,67 @@ +/* + * 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.io.serialization; + +import net.openhft.lang.io.DirectBytes; +import net.openhft.lang.io.DirectStore; +import net.openhft.lang.io.NativeBytes; +import org.junit.Test; +import sun.nio.ch.DirectBuffer; + +import java.nio.ByteBuffer; + +import static org.junit.Assert.assertEquals; + +/** + * User: peter.lawrey Date: 20/09/13 Time: 09:28 + */ +public class VanillaBytesMarshallerTest { + @Test + public void testObjects() { + DirectBytes bytes = new DirectStore(1024).createSlice(); + 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(); + for (Object o : objects) { + Object o2 = bytes.readObject(); + assertEquals(o, o2); + } + } + + @Test + public void testMarshallable() { + int capacity = 2 * 1024; + ByteBuffer byteBuffer = ByteBuffer.allocateDirect(capacity); + long addr = ((DirectBuffer) byteBuffer).address(); + NativeBytes nativeBytes = new NativeBytes(addr, addr, addr + capacity); + + nativeBytes.writeObject(BuySell.BUY); + nativeBytes.writeObject(BuySell.SELL); + nativeBytes.finish(); + nativeBytes.reset(); + assertEquals(BuySell.BUY, nativeBytes.readObject()); + assertEquals(BuySell.SELL, nativeBytes.readObject()); + } + + enum BuySell { + BUY, SELL + } +} diff --git a/lang/src/test/java/net/openhft/lang/io/serialization/direct/DirectSerializationFilterTest.java b/lang/src/test/java/net/openhft/lang/io/serialization/direct/DirectSerializationFilterTest.java new file mode 100644 index 0000000..35b5c6e --- /dev/null +++ b/lang/src/test/java/net/openhft/lang/io/serialization/direct/DirectSerializationFilterTest.java @@ -0,0 +1,94 @@ +package net.openhft.lang.io.serialization.direct; + +import org.junit.Test; + +import java.lang.reflect.Field; +import java.util.*; + +import static net.openhft.lang.io.serialization.direct.DirectSerializationFilter.stopAtFirstIneligibleField; +import static org.junit.Assert.*; + +public class DirectSerializationFilterTest { + + @Test + public void instancePrimitivesAreEligible() { + List<Field> field = fieldsNamed("intField"); + assertEquals(field, stopAtFirstIneligibleField(field)); + } + + @Test + public void instancePrimitiveArraysAreNotEligible() { + List<Field> field = fieldsNamed("doubleArray"); + assertTrue(stopAtFirstIneligibleField(field).isEmpty()); + } + + @Test + public void instanceReferencesAreNotEligible() { + List<Field> field = fieldsNamed("stringList"); + assertTrue(stopAtFirstIneligibleField(field).isEmpty()); + } + + @Test + public void instanceReferenceArraysAreNotEligible() { + List<Field> field = fieldsNamed("objectArray"); + assertTrue(stopAtFirstIneligibleField(field).isEmpty()); + } + + @Test + public void staticPrimitivesAreNotEligible() { + List<Field> field = fieldsNamed("staticIntField"); + assertTrue(stopAtFirstIneligibleField(field).isEmpty()); + } + + @Test + public void staticPrimitiveArraysAreNotEligible() { + List<Field> field = fieldsNamed("staticDoubleArray"); + assertTrue(stopAtFirstIneligibleField(field).isEmpty()); + } + + @Test + public void staticReferencesAreNotEligible() { + List<Field> field = fieldsNamed("staticStringList"); + assertTrue(stopAtFirstIneligibleField(field).isEmpty()); + } + + @Test + public void staticReferenceArraysAreNotEligible() { + List<Field> field = fieldsNamed("staticObjectArray"); + assertTrue(stopAtFirstIneligibleField(field).isEmpty()); + } + + @Test + public void transientPrimitivesAreNotEligible() { + List<Field> field = fieldsNamed("transientShort"); + assertTrue(stopAtFirstIneligibleField(field).isEmpty()); + } + + @Test + public void transientReferencsAreNotEligible() { + List<Field> field = fieldsNamed("transientObject"); + assertTrue(stopAtFirstIneligibleField(field).isEmpty()); + } + + @Test + public void includesAllEligibleFields() { + List<Field> fields = fieldsNamed("intField", "shortField", "longField", "byteField", "stringList"); + List<Field> expectedfields = fields.subList(0, 4); + + assertEquals(expectedfields, stopAtFirstIneligibleField(fields)); + } + + private static List<Field> fieldsNamed(String... names) { + ArrayList<Field> fields = new ArrayList<Field>(); + + for (String name : names) { + try { + fields.add(TestClasses.MixedFields.class.getDeclaredField(name)); + } catch (NoSuchFieldException e) { + throw new RuntimeException("Exception retrieving " + name, e); + } + } + + return fields; + } +} diff --git a/lang/src/test/java/net/openhft/lang/io/serialization/direct/DirectSerializationMetadataTest.java b/lang/src/test/java/net/openhft/lang/io/serialization/direct/DirectSerializationMetadataTest.java new file mode 100644 index 0000000..1c02082 --- /dev/null +++ b/lang/src/test/java/net/openhft/lang/io/serialization/direct/DirectSerializationMetadataTest.java @@ -0,0 +1,78 @@ +package net.openhft.lang.io.serialization.direct; + +import org.junit.Test; + +import static net.openhft.lang.io.AbstractBytes.UNSIGNED_INT_MASK; +import static net.openhft.lang.io.NativeBytes.UNSAFE; +import static net.openhft.lang.io.serialization.direct.DirectSerializationMetadata.*; +import static net.openhft.lang.io.serialization.direct.TestClasses.*; +import static org.junit.Assert.assertEquals; + +public class DirectSerializationMetadataTest { + + @Test + public void primitives1Metadata() { + unsafeClassHeaderSize64BitCompressedOops(new Primitives1()); + + SerializationMetadata serializationMetadata = extractMetadata(Introspect.fields(Primitives1.class)); + assertEquals(OBJECT_HEADER_SIZE, serializationMetadata.start); + assertEquals(4, serializationMetadata.length); + } + + @Test + public void primitives2Metadata() { + unsafeClassHeaderSize64BitCompressedOops(new Primitives2()); + + SerializationMetadata serializationMetadata = extractMetadata(Introspect.fields(Primitives2.class)); + assertEquals(OBJECT_HEADER_SIZE, serializationMetadata.start); + assertEquals(12, serializationMetadata.length); + } + + @Test + public void primitives3Metadata() { + unsafeClassHeaderSize64BitCompressedOops(new Primitives3()); + + SerializationMetadata serializationMetadata = extractMetadata(Introspect.fields(Primitives3.class)); + assertEquals(OBJECT_HEADER_SIZE, serializationMetadata.start); + assertEquals(12, serializationMetadata.length); + } + + @Test + public void primitives4Metadata() { + unsafeClassHeaderSize64BitCompressedOops(new Primitives4()); + + SerializationMetadata serializationMetadata = extractMetadata(Introspect.fields(Primitives4.class)); + assertEquals(OBJECT_HEADER_SIZE, serializationMetadata.start); + assertEquals(4, serializationMetadata.length); + } + + @Test + public void primitives5Metadata() { + unsafeClassHeaderSize64BitCompressedOops(new Primitives5()); + + SerializationMetadata serializationMetadata = extractMetadata(Introspect.fields(Primitives5.class)); + assertEquals(OBJECT_HEADER_SIZE, serializationMetadata.start); + assertEquals(12, serializationMetadata.length); + } + + @Test + public void primitives6Metadata() { + unsafeClassHeaderSize64BitCompressedOops(new Primitives6()); + + SerializationMetadata serializationMetadata = extractMetadata(Introspect.fields(Primitives6.class)); + assertEquals(OBJECT_HEADER_SIZE, serializationMetadata.start); + assertEquals(28, serializationMetadata.length); + } + + private void unsafeClassHeaderSize64BitCompressedOops(Object instance) { + if (System.getProperty("useUnsafeClassHeaders") != null) { + long narrowKlassPointer = UNSAFE.getInt(instance, NATIVE_WORD_SIZE) & UNSIGNED_INT_MASK; + long wideKlassPointer = narrowKlassPointer << 3; + long sizeField = wideKlassPointer + 3 * UNSAFE.addressSize(); + + System.out.println(String.format("Size of %s is %s", + instance.getClass().getName(), + UNSAFE.getAddress(sizeField) & UNSIGNED_INT_MASK)); + } + } +} diff --git a/lang/src/test/java/net/openhft/lang/io/serialization/direct/IntrospectTest.java b/lang/src/test/java/net/openhft/lang/io/serialization/direct/IntrospectTest.java new file mode 100644 index 0000000..1c5879b --- /dev/null +++ b/lang/src/test/java/net/openhft/lang/io/serialization/direct/IntrospectTest.java @@ -0,0 +1,38 @@ +package net.openhft.lang.io.serialization.direct; + +import org.junit.Test; + +import java.lang.reflect.Field; +import java.util.*; + +import static net.openhft.lang.io.serialization.direct.TestClasses.*; +import static org.junit.Assert.assertEquals; + +public class IntrospectTest { + + @Test + public void returnsFieldsRegardlessOfVisibility() { + List<Field> fields = asFieldList(InstanceOnlyNoStaticFields.class); + + assertEquals(3, fields.size()); + } + + @Test + public void returnsStaticAsWellAsInstanceFields() { + List<Field> fields = asFieldList(HasInstanceAndStaticFields.class); + + assertEquals(6, fields.size()); + } + + @Test + public void walksClassHierarchyForAllFields() { + List<Field> fields = asFieldList(LevelTwoDerivedClass.class); + + assertEquals(6, fields.size()); + } + + private static List<Field> asFieldList(Class<?> clazz) { + return new ArrayList<Field>(Introspect.fields(clazz)); + } +} + diff --git a/lang/src/test/java/net/openhft/lang/io/serialization/direct/ObjectMarshallerTest.java b/lang/src/test/java/net/openhft/lang/io/serialization/direct/ObjectMarshallerTest.java new file mode 100644 index 0000000..7226a17 --- /dev/null +++ b/lang/src/test/java/net/openhft/lang/io/serialization/direct/ObjectMarshallerTest.java @@ -0,0 +1,245 @@ +package net.openhft.lang.io.serialization.direct; + +import net.openhft.lang.io.*; +import org.junit.Test; + +import java.nio.ByteBuffer; +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 { + + @Test + public void marshalAndUnmarshalPrimitives1() { + Primitives1 p = new Primitives1(); + p.a = 55; + + Bytes b = createByteStore(); + ObjectMarshaller<Primitives1> marshaller = ObjectMarshallers.forClass(Primitives1.class); + marshaller.write(b, p); + + p.a = 0; + b.reset(); + marshaller.read(b, p); + + assertEquals(55, p.a); + + b.reset(); + + Primitives1 newP = new Primitives1(); + marshaller.read(b, newP); + + assertEquals(55, newP.a); + } + + @Test + public void marshalAndUnmarshalPrimitives2() { + Primitives2 p = new Primitives2(); + p.a = 55; + p.b = -10; + + Bytes b = createByteStore(); + ObjectMarshaller<Primitives2> marshaller = ObjectMarshallers.forClass(Primitives2.class); + marshaller.write(b, p); + + p.a = 0; + p.b = 0; + b.reset(); + marshaller.read(b, p); + + assertEquals(55, p.a); + assertEquals(-10, p.b); + + b.reset(); + + Primitives2 newP = new Primitives2(); + marshaller.read(b, newP); + + assertEquals(55, newP.a); + assertEquals(-10, newP.b); + } + + @Test + public void marshalAndUnmarshalPrimitives3() { + Primitives3 p = new Primitives3(); + p.a = 55; + p.b = -10; + p.c = 92; + + Bytes b = createByteStore(); + ObjectMarshaller<Primitives3> marshaller = ObjectMarshallers.forClass(Primitives3.class); + marshaller.write(b, p); + + p.a = 0; + p.b = 0; + p.c = 0; + b.reset(); + marshaller.read(b, p); + + assertEquals(55, p.a); + assertEquals(-10, p.b); + assertEquals(92, p.c); + + b.reset(); + + Primitives3 newP = new Primitives3(); + marshaller.read(b, newP); + + assertEquals(55, newP.a); + assertEquals(-10, newP.b); + assertEquals(92, newP.c); + } + + @Test + public void marshalAndUnmarshalPrimitives4() { + Primitives4 p = new Primitives4(); + p.a = true; + + Bytes b = createByteStore(); + ObjectMarshaller<Primitives4> marshaller = ObjectMarshallers.forClass(Primitives4.class); + marshaller.write(b, p); + + p.a = false; + b.reset(); + marshaller.read(b, p); + + assertTrue(p.a); + + b.reset(); + + Primitives4 newP = new Primitives4(); + marshaller.read(b, newP); + + assertTrue(newP.a); + } + + @Test + public void marshalAndUnmarshalPrimitives5() { + Primitives5 p = new Primitives5(); + p.a = true; + p.b = Long.MIN_VALUE; + + Bytes b = createByteStore(); + ObjectMarshaller<Primitives5> marshaller = ObjectMarshallers.forClass(Primitives5.class); + marshaller.write(b, p); + + p.a = false; + p.b = 0; + b.reset(); + marshaller.read(b, p); + + assertTrue(p.a); + assertEquals(Long.MIN_VALUE, p.b); + + b.reset(); + + Primitives5 newP = new Primitives5(); + marshaller.read(b, newP); + + assertTrue(newP.a); + assertEquals(Long.MIN_VALUE, newP.b); + } + + @Test + public void marshalAndUnmarshalPrimitives6() { + Primitives6 p = new Primitives6(); + p.a = true; + p.b = Integer.MAX_VALUE; + p.c = Short.MAX_VALUE; + p.d = Long.MAX_VALUE; + p.e = Double.MAX_VALUE; + + Bytes b = createByteStore(); + ObjectMarshaller<Primitives6> marshaller = ObjectMarshallers.forClass(Primitives6.class); + marshaller.write(b, p); + + p.a = false; + p.b = 0; + p.c = 0; + p.d = 0; + p.e = 0; + b.reset(); + marshaller.read(b, p); + + assertTrue(p.a); + assertEquals(Integer.MAX_VALUE, p.b); + assertEquals(Short.MAX_VALUE, p.c); + assertEquals(Long.MAX_VALUE, p.d); + assertEquals(Double.MAX_VALUE, p.e, 0); + + b.reset(); + + Primitives6 newP = new Primitives6(); + marshaller.read(b, newP); + + assertTrue(newP.a); + assertEquals(Integer.MAX_VALUE, p.b); + assertEquals(Short.MAX_VALUE, p.c); + assertEquals(Long.MAX_VALUE, p.d); + assertEquals(Double.MAX_VALUE, p.e, 0); + } + + @Test + public void marshalAndUnmarshalMixedClass() { + MixedFields m = new MixedFields(); + m.intField = Integer.MIN_VALUE + 1; + m.byteField = Byte.MAX_VALUE - 1; + m.shortField = Short.MIN_VALUE + 1; + m.longField = Long.MAX_VALUE - 1; + + m.doubleArray = new double[]{-50.0, 50.0}; + m.stringList = Collections.singletonList("Foo"); + m.objectArray = new Object[]{new Primitives1()}; + m.transientShort = 12; + m.transientObject = new Primitives2(); + + Bytes b = createByteStore(); + ObjectMarshaller<MixedFields> marshaller = ObjectMarshallers.forClass(MixedFields.class); + marshaller.write(b, m); + + m.intField = 0; + m.byteField = 0; + m.shortField = 0; + m.longField = 0; + m.doubleArray = null; + m.stringList = null; + m.objectArray = null; + m.transientShort = 0; + m.transientObject = null; + + b.reset(); + marshaller.read(b, m); + + assertEquals(Integer.MIN_VALUE + 1, m.intField); + assertEquals(0, m.byteField); // because of jvm field rearrangement the two shorts are packed into 4 bytes and this eligible field is skipped + assertEquals(Short.MIN_VALUE + 1, m.shortField); + assertEquals(Long.MAX_VALUE - 1, m.longField); + assertNull(m.doubleArray); + assertNull(m.stringList); + assertNull(m.objectArray); + assertNull(m.transientObject); + assertEquals(0, m.transientShort); + + b.reset(); + + MixedFields newM = new MixedFields(); + marshaller.read(b, newM); + + assertEquals(Integer.MIN_VALUE + 1, newM.intField); + assertEquals(0, newM.byteField); + assertEquals(Short.MIN_VALUE + 1, newM.shortField); + assertEquals(Long.MAX_VALUE - 1, newM.longField); + assertNull(newM.doubleArray); + assertNull(newM.stringList); + assertNull(newM.objectArray); + assertNull(newM.transientObject); + assertEquals(0, newM.transientShort); + } + + private ByteBufferBytes createByteStore() { + return new ByteBufferBytes(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/src/test/java/net/openhft/lang/io/serialization/direct/TestClasses.java new file mode 100644 index 0000000..7b173dd --- /dev/null +++ b/lang/src/test/java/net/openhft/lang/io/serialization/direct/TestClasses.java @@ -0,0 +1,90 @@ +package net.openhft.lang.io.serialization.direct; + +import java.util.*; + +class TestClasses { + + public static class InstanceOnlyNoStaticFields { + public int intField; + protected String stringField; + private Object objectField; + } + + public static class HasInstanceAndStaticFields { + public static int staticIntField; + protected static String staticStringField; + private static Object staticObjectField; + + public int intField; + protected String stringField; + private Object objectField; + } + + public static class BaseClass { + private static double staticBaseDoubleField; + + protected List<String> baseListField; + } + + public static class LevelOneDerivedClass extends BaseClass { + protected static Object staticLevelOneObjectField; + + public long levelOneLongField; + } + + public static class LevelTwoDerivedClass extends LevelOneDerivedClass { + public static String staticLevelTwoStringField; + + final int levelTwoDerivedIntField = 0; + } + + public static class MixedFields { + public static int staticIntField ; + public static double[] staticDoubleArray; + + public static List<String> staticStringList = Collections.singletonList("S1"); + public static Object[] staticObjectArray = {new Object()}; + + public int intField; + public byte byteField; + public short shortField; + public long longField; + public double[] doubleArray; + + public List<String> stringList; + public Object[] objectArray; + + transient short transientShort; + public transient Object transientObject; + } + + public static class Primitives1 { + public int a; + } + + public static class Primitives2 { + public int a, b; + } + + public static class Primitives3 { + public int a, b; + public short c; + } + + public static class Primitives4 { + public boolean a; + } + + public static class Primitives5 { + public boolean a; + public long b; + } + + public static class Primitives6 { + public boolean a; + public int b; + public short c; + public long d; + public double e; + } +} diff --git a/lang/src/test/java/net/openhft/lang/model/DataValueGeneratorTest.java b/lang/src/test/java/net/openhft/lang/model/DataValueGeneratorTest.java new file mode 100644 index 0000000..1310fde --- /dev/null +++ b/lang/src/test/java/net/openhft/lang/model/DataValueGeneratorTest.java @@ -0,0 +1,174 @@ +/* + * 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.model; + +import net.openhft.compiler.CachedCompiler; +import net.openhft.lang.io.ByteBufferBytes; +import net.openhft.lang.io.Bytes; +import org.junit.Test; + +import java.nio.ByteBuffer; + +import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertTrue; + +/** + * User: peter.lawrey Date: 06/10/13 Time: 20:13 + */ +public class DataValueGeneratorTest { + @Test + public void testGenerateJavaCode() throws Exception { + DataValueGenerator dvg = new DataValueGenerator(); +// dvg.setDumpCode(true); + JavaBeanInterface jbi = dvg.heapInstance(JavaBeanInterface.class); + 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(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()); + } + + @Test + public void testGenerateJavaCode2() throws Exception { + DataValueGenerator dvg = new DataValueGenerator(); + MinimalInterface mi = dvg.heapInstance(MinimalInterface.class); + + mi.byte$((byte) 1); + mi.char$('2'); + mi.short$((short) 3); + mi.int$(4); + mi.float$(5); + mi.long$(6); + mi.double$(7); + mi.flag(true); + + assertEquals(1, mi.byte$()); + assertEquals('2', mi.char$()); + assertEquals(3, mi.short$()); + assertEquals(4, mi.int$()); + assertEquals(5.0, mi.float$(), 0); + assertEquals(6, mi.long$()); + assertEquals(7.0, mi.double$(), 0.0); + assertTrue(mi.flag()); + + ByteBufferBytes bbb = new ByteBufferBytes(ByteBuffer.allocate(64)); + mi.writeMarshallable(bbb); + System.out.println("size: " + bbb.position()); + + MinimalInterface mi2 = dvg.heapInstance(MinimalInterface.class); + bbb.position(0); + mi2.readMarshallable(bbb); + + + assertEquals(1, mi2.byte$()); + assertEquals('2', mi2.char$()); + assertEquals(3, mi2.short$()); + assertEquals(4, mi2.int$()); + assertEquals(5.0, mi2.float$(), 0); + assertEquals(6, mi2.long$()); + assertEquals(7.0, mi2.double$(), 0.0); + assertTrue(mi2.flag()); + } + + @Test + public void testGenerateNative() throws Exception { + String actual = new DataValueGenerator().generateNativeObject(JavaBeanInterface.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()); + 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()); + } + + + @Test + public void testStringFields() { + DataValueGenerator dvg = new DataValueGenerator(); + StringInterface si = dvg.heapInstance(StringInterface.class); + si.setString("Hello world"); + assertEquals("Hello world", si.getString()); + + StringInterface si2 = dvg.nativeInstance(StringInterface.class); + Bytes bytes = new ByteBufferBytes(ByteBuffer.allocate(192)); + ((Byteable) si2).bytes(bytes); + si2.setString("Hello world £€"); + si2.setText("Hello world £€"); + assertEquals("Hello world £€", si2.getString()); + assertEquals("Hello world £€", si2.getText()); + } + + @Test + public void testNested() { + DataValueGenerator dvg = new DataValueGenerator(); +// dvg.setDumpCode(true); + NestedB nestedB1 = dvg.heapInstance(NestedB.class); + nestedB1.ask(100); + nestedB1.bid(100); + NestedB nestedB2 = dvg.heapInstance(NestedB.class); + nestedB2.ask(91); + nestedB2.bid(92); + +// dvg.setDumpCode(true); + NestedA nestedA = dvg.nativeInstance(NestedA.class); + Bytes bytes = new ByteBufferBytes(ByteBuffer.allocate(192)); + ((Byteable) nestedA).bytes(bytes); + nestedA.key("key"); + nestedA.one(nestedB1); + nestedA.two(nestedB2); + assertEquals("key", nestedA.key()); + assertEquals(nestedB1.ask(), nestedA.one().ask(), 0.0); + assertEquals(nestedB1.bid(), nestedA.one().bid(), 0.0); + assertEquals(nestedB2.ask(), nestedA.two().ask(), 0.0); + assertEquals(nestedB2.bid(), nestedA.two().bid(), 0.0); + assertEquals(nestedB1, nestedA.one()); + assertEquals(nestedB2, nestedA.two()); + assertEquals(nestedB1.hashCode(), nestedA.one().hashCode()); + assertEquals(nestedB2.hashCode(), nestedA.two().hashCode()); + } +} diff --git a/lang/src/test/java/net/openhft/lang/model/DataValueModelTest.java b/lang/src/test/java/net/openhft/lang/model/DataValueModelTest.java new file mode 100644 index 0000000..0f7a8f0 --- /dev/null +++ b/lang/src/test/java/net/openhft/lang/model/DataValueModelTest.java @@ -0,0 +1,59 @@ +/* + * 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.model; + +import org.junit.Test; + +import static org.junit.Assert.assertEquals; + +/** + * User: peter.lawrey Date: 06/10/13 Time: 18:12 + */ +public class DataValueModelTest { + @Test + public void testAcquire() { + DataValueModel<MinimalInterface> midvm = DataValueModels.acquireModel(MinimalInterface.class); + assertEquals("{byte$=FieldModel{name='byte$', getter=public abstract byte net.openhft.lang.model.MinimalInterface.byte$(), setter=public abstract void net.openhft.lang.model.MinimalInterface.byte$(byte)}\n" + + " char$=FieldModel{name='char$', getter=public abstract char net.openhft.lang.model.MinimalInterface.char$(), setter=public abstract void net.openhft.lang.model.MinimalInterface.char$(char)}\n" + + " double$=FieldModel{name='double$', getter=public abstract double net.openhft.lang.model.MinimalInterface.double$(), setter=public abstract void net.openhft.lang.model.MinimalInterface.double$(double)}\n" + + " flag=FieldModel{name='flag', getter=public abstract boolean net.openhft.lang.model.MinimalInterface.flag(), setter=public abstract void net.openhft.lang.model.MinimalInterface.flag(boolean)}\n" + + " float$=FieldModel{name='float$', getter=public abstract float net.openhft.lang.model.MinimalInterface.float$(), setter=public abstract void net.openhft.lang.model.MinimalInterface.float$(float)}\n" + + " int$=FieldModel{name='int$', getter=public abstract int net.openhft.lang.model.MinimalInterface.int$(), setter=public abstract void net.openhft.lang.model.MinimalInterface.int$(int)}\n" + + " long$=FieldModel{name='long$', getter=public abstract long net.openhft.lang.model.MinimalInterface.long$(), setter=public abstract void net.openhft.lang.model.MinimalInterface.long$(long)}\n" + + " 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" + + " 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" + + " 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" + + " 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")); + + } +} diff --git a/lang/src/test/java/net/openhft/lang/model/JavaBeanInterface.java b/lang/src/test/java/net/openhft/lang/model/JavaBeanInterface.java new file mode 100644 index 0000000..0cf0675 --- /dev/null +++ b/lang/src/test/java/net/openhft/lang/model/JavaBeanInterface.java @@ -0,0 +1,70 @@ +/* + * 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.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; + + void unlockRecord(); + + void setFlag(boolean flag); + + boolean getFlag(); + + void setByte(byte b); + + byte getByte(); + + void setShort(short s); + + short getShort(); + + void setChar(char ch); + + char getChar(); + + void setInt(int i); + + int getInt(); + + void setFloat(float f); + + float getFloat(); + + void setLong(long l); + + long getLong(); + + void setDouble(double d); + + double getDouble(); + + void setString(@MaxSize(8) String s); + + String getString(); +} diff --git a/lang/src/test/java/net/openhft/lang/model/MinimalInterface.java b/lang/src/test/java/net/openhft/lang/model/MinimalInterface.java new file mode 100644 index 0000000..2379b2e --- /dev/null +++ b/lang/src/test/java/net/openhft/lang/model/MinimalInterface.java @@ -0,0 +1,58 @@ +/* + * 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.model; + +import net.openhft.lang.io.serialization.BytesMarshallable; + +/** + * User: peter.lawrey + * Date: 06/10/13 + * Time: 16:59 + */ +public interface MinimalInterface extends BytesMarshallable, Copyable<MinimalInterface>, Byteable { + void flag(boolean flag); + + boolean flag(); + + void byte$(byte b); + + byte byte$(); + + void short$(short s); + + short short$(); + + void char$(char ch); + + char char$(); + + void int$(int i); + + int int$(); + + void float$(float f); + + float float$(); + + void long$(long l); + + long long$(); + + void double$(double d); + + double double$(); +} diff --git a/lang/src/test/java/net/openhft/lang/model/NestedA.java b/lang/src/test/java/net/openhft/lang/model/NestedA.java new file mode 100644 index 0000000..70d5f8a --- /dev/null +++ b/lang/src/test/java/net/openhft/lang/model/NestedA.java @@ -0,0 +1,39 @@ +/* + * 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.model; + +import net.openhft.lang.model.constraints.MaxSize; + +/** + * User: peter.lawrey + * Date: 08/10/13 + * Time: 10:11 + */ +public interface NestedA { + void key(@MaxSize String key); + + String key(); + + void one(NestedB one); + + NestedB one(); + + 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 new file mode 100644 index 0000000..22caef0 --- /dev/null +++ b/lang/src/test/java/net/openhft/lang/model/NestedArrayInterface.java @@ -0,0 +1,35 @@ +/* + * 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.model; + +import net.openhft.lang.model.constraints.MaxSize; + +/** + */ +public interface NestedArrayInterface { + String getText(); + + void setText(String text); + + int getInt(int index); + + void setInt(@MaxSize(16) int index, int value); + + JavaBeanInterface getJBI(@MaxSize(32) int index); + + void setJBI(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 new file mode 100644 index 0000000..5b4807b --- /dev/null +++ b/lang/src/test/java/net/openhft/lang/model/NestedB.java @@ -0,0 +1,34 @@ +/* + * 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.model; + +import net.openhft.lang.model.constraints.Digits; + +/** + * User: peter.lawrey + * Date: 08/10/13 + * Time: 10:24 + */ +public interface NestedB { + void bid(@Digits double bid); + + double bid(); + + void ask(@Digits double ask); + + double ask(); +} diff --git a/lang/src/test/java/net/openhft/lang/model/StringInterface.java b/lang/src/test/java/net/openhft/lang/model/StringInterface.java new file mode 100644 index 0000000..14d5d19 --- /dev/null +++ b/lang/src/test/java/net/openhft/lang/model/StringInterface.java @@ -0,0 +1,34 @@ +/* + * 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.model; + +import net.openhft.lang.model.constraints.MaxSize; + +/** + * User: peter.lawrey + * Date: 08/10/13 + * Time: 09:09 + */ +public interface StringInterface { + void setString(@MaxSize(64) String s); + + String getString(); + + void setText(@MaxSize String s); + + String getText(); +} diff --git a/lang/src/test/java/net/openhft/lang/testing/RunningMinimumTest.java b/lang/src/test/java/net/openhft/lang/testing/RunningMinimumTest.java new file mode 100644 index 0000000..a6c0ddb --- /dev/null +++ b/lang/src/test/java/net/openhft/lang/testing/RunningMinimumTest.java @@ -0,0 +1,50 @@ +/* + * 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.testing; + +import org.junit.Test; + +import static org.junit.Assert.assertEquals; + +/** + * User: peter.lawrey + * Date: 05/08/13 + * Time: 19:14 + */ +public class RunningMinimumTest { + @Test + public void testSample() throws Exception { + 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); + int j; + for (j = 0; j < 50 * 1000000; j += 1000000) { + long startTime = System.nanoTime() + j; + long endTime = System.nanoTime() + j + delta + (long) (Math.pow(10 * 1000, Math.random()) * 1000); + rm.sample(startTime, endTime); + } + assertEquals("delta=" + delta, delta, rm.minimum(), 40 * 1000); + } + } + } + + @Test + public void testVanillaDiff() { + VanillaDifferencer vd = new VanillaDifferencer(); + assertEquals(100, vd.sample(123400, 123500)); + } +} diff --git a/lang/src/test/java/net/openhft/lang/values/CheckValuesBuildTest.java b/lang/src/test/java/net/openhft/lang/values/CheckValuesBuildTest.java new file mode 100644 index 0000000..f10ae6e --- /dev/null +++ b/lang/src/test/java/net/openhft/lang/values/CheckValuesBuildTest.java @@ -0,0 +1,51 @@ +/* + * 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.values; + +import net.openhft.lang.model.DataValueGenerator; +import org.junit.Test; + +/** + * User: peter.lawrey + * Date: 10/10/13 + * Time: 11:38 + */ +public class CheckValuesBuildTest { + @Test + public void testValuesCompile() throws ClassNotFoundException { + DataValueGenerator dvg = new DataValueGenerator(); + for (Class clazz : new Class[]{ + IntValue.class, + ByteValue.class, + CharValue.class, + ShortValue.class, + Int24Value.class, + FloatValue.class, + DoubleValue.class, + LongValue.class, + StringValue.class, + BooleanValue.class, + UnsignedByteValue.class, + UnsignedIntValue.class, + UnsignedShortValue.class, + NestAll.class + }) { + System.out.println(dvg.acquireHeapClass(clazz).getName() + " " + + dvg.acquireNativeClass(clazz).getName()); + } + } +} diff --git a/lang/src/test/java/net/openhft/lang/values/NestAll.java b/lang/src/test/java/net/openhft/lang/values/NestAll.java new file mode 100644 index 0000000..cb629b6 --- /dev/null +++ b/lang/src/test/java/net/openhft/lang/values/NestAll.java @@ -0,0 +1,53 @@ +/* + * 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.values; + +/** + * User: peter.lawrey + * Date: 11/10/13 + * Time: 08:59 + */ +public interface NestAll { + BooleanValue getBoolean(); + + ByteValue getByte(); + + CharValue getChar(); + + DoubleValue getDouble(); + + FloatValue getFloat(); + + Int24Value getInt24(); + + Int48Value getInt48(); + + IntValue getInt(); + + LongValue getLong(); + + ShortValue getShort(); + + StringValue getString(); + + UnsignedByteValue getUB(); + + UnsignedIntValue getUI(); + + UnsignedShortValue getUS(); + +} @@ -0,0 +1,36 @@ +<?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>Java-Lang</artifactId> + <version>6.1.3</version> + <packaging>pom</packaging> + + <name>Java Lang Parent</name> + <description>High Performance Java Lang library</description> + + <modules> + <module>lang</module> + <module>lang-osgi</module> + <module>lang-integration</module> + </modules> +</project>
\ No newline at end of file |