summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorEmmanuel Bourg <ebourg@apache.org>2015-07-17 00:28:58 +0200
committerEmmanuel Bourg <ebourg@apache.org>2015-07-17 00:28:58 +0200
commitdadc5626cd17e6dc4c4cc9fa814fbe2a65ac77d5 (patch)
tree266aefd3798c3db8c847e5e4f0be4d3ec4753bd9
parent1b08101f81e5790c213a07dce165169861ef9d95 (diff)
parentb2ec1a2d459cfef3ff13133c1f7f5972e3740258 (diff)
Merge tag 'upstream/6.1.4'
Upstream version 6.1.4
-rw-r--r--.gitignore13
-rw-r--r--.idea/copyright/Apache_2_0.xml9
-rw-r--r--README.md74
-rwxr-xr-xlang-integration/pom.xml125
-rw-r--r--lang-integration/src/test/java/net/openhft/lang/io/LockingViaMMapWithThreadIdMain.java104
-rwxr-xr-xlang-osgi/pom.xml215
-rw-r--r--lang-osgi/src/main/java/net/openhft/langosgi/model/JavaBeanInterface.java68
-rw-r--r--lang-osgi/src/test/java/net/openhft/lang/osgi/OSGiBundleTest.java89
-rw-r--r--lang-osgi/src/test/java/net/openhft/lang/osgi/OSGiCollectionTest.java74
-rw-r--r--lang/.gitignore3
-rwxr-xr-xlang/pom.xml175
-rw-r--r--lang/src/main/java/net/openhft/lang/Compare.java176
-rwxr-xr-xlang/src/main/java/net/openhft/lang/Jvm.java90
-rw-r--r--lang/src/main/java/net/openhft/lang/LongHashable.java26
-rwxr-xr-xlang/src/main/java/net/openhft/lang/Maths.java154
-rw-r--r--lang/src/main/java/net/openhft/lang/collection/HugeArray.java68
-rw-r--r--lang/src/main/java/net/openhft/lang/collection/HugeCollections.java48
-rw-r--r--lang/src/main/java/net/openhft/lang/collection/HugeQueue.java69
-rw-r--r--lang/src/main/java/net/openhft/lang/collection/impl/HugeArrayImpl.java111
-rw-r--r--lang/src/main/java/net/openhft/lang/collection/impl/HugeQueueImpl.java89
-rwxr-xr-xlang/src/main/java/net/openhft/lang/io/AbstractBytes.java2323
-rwxr-xr-xlang/src/main/java/net/openhft/lang/io/ByteBufferBytes.java441
-rwxr-xr-xlang/src/main/java/net/openhft/lang/io/ByteStringAppender.java80
-rwxr-xr-xlang/src/main/java/net/openhft/lang/io/ByteStringParser.java83
-rwxr-xr-xlang/src/main/java/net/openhft/lang/io/Bytes.java26
-rwxr-xr-xlang/src/main/java/net/openhft/lang/io/BytesCommon.java104
-rwxr-xr-xlang/src/main/java/net/openhft/lang/io/DirectBytes.java43
-rwxr-xr-xlang/src/main/java/net/openhft/lang/io/DirectStore.java96
-rwxr-xr-xlang/src/main/java/net/openhft/lang/io/IOTools.java79
-rw-r--r--lang/src/main/java/net/openhft/lang/io/MappedFile.java146
-rw-r--r--lang/src/main/java/net/openhft/lang/io/MappedMemory.java83
-rw-r--r--lang/src/main/java/net/openhft/lang/io/MultiStoreBytes.java40
-rwxr-xr-xlang/src/main/java/net/openhft/lang/io/MutableDecimal.java205
-rwxr-xr-xlang/src/main/java/net/openhft/lang/io/NativeBytes.java416
-rwxr-xr-xlang/src/main/java/net/openhft/lang/io/RandomDataInput.java868
-rwxr-xr-xlang/src/main/java/net/openhft/lang/io/RandomDataOutput.java819
-rw-r--r--lang/src/main/java/net/openhft/lang/io/RandomDataUpdate.java141
-rwxr-xr-xlang/src/main/java/net/openhft/lang/io/StopCharTester.java35
-rwxr-xr-xlang/src/main/java/net/openhft/lang/io/StopCharTesters.java93
-rw-r--r--lang/src/main/java/net/openhft/lang/io/serialization/BytesMarshallable.java43
-rw-r--r--lang/src/main/java/net/openhft/lang/io/serialization/BytesMarshaller.java45
-rw-r--r--lang/src/main/java/net/openhft/lang/io/serialization/BytesMarshallerFactory.java31
-rw-r--r--lang/src/main/java/net/openhft/lang/io/serialization/CompactBytesMarshaller.java27
-rw-r--r--lang/src/main/java/net/openhft/lang/io/serialization/RawCopier.java104
-rw-r--r--lang/src/main/java/net/openhft/lang/io/serialization/direct/DirectSerializationFilter.java29
-rw-r--r--lang/src/main/java/net/openhft/lang/io/serialization/direct/DirectSerializationMetadata.java89
-rw-r--r--lang/src/main/java/net/openhft/lang/io/serialization/direct/FieldMetadata.java22
-rw-r--r--lang/src/main/java/net/openhft/lang/io/serialization/direct/Introspect.java63
-rw-r--r--lang/src/main/java/net/openhft/lang/io/serialization/direct/ObjectMarshaller.java50
-rw-r--r--lang/src/main/java/net/openhft/lang/io/serialization/direct/ObjectMarshallers.java72
-rw-r--r--lang/src/main/java/net/openhft/lang/io/serialization/impl/BytesMarshallableMarshaller.java52
-rw-r--r--lang/src/main/java/net/openhft/lang/io/serialization/impl/ClassMarshaller.java105
-rw-r--r--lang/src/main/java/net/openhft/lang/io/serialization/impl/CompactEnumBytesMarshaller.java38
-rw-r--r--lang/src/main/java/net/openhft/lang/io/serialization/impl/DateMarshaller.java95
-rw-r--r--lang/src/main/java/net/openhft/lang/io/serialization/impl/EnumBytesMarshaller.java93
-rw-r--r--lang/src/main/java/net/openhft/lang/io/serialization/impl/ExternalizableMarshaller.java58
-rw-r--r--lang/src/main/java/net/openhft/lang/io/serialization/impl/GenericEnumMarshaller.java93
-rw-r--r--lang/src/main/java/net/openhft/lang/io/serialization/impl/NoMarshaller.java38
-rw-r--r--lang/src/main/java/net/openhft/lang/io/serialization/impl/StringMarshaller.java60
-rw-r--r--lang/src/main/java/net/openhft/lang/io/serialization/impl/VanillaBytesMarshallerFactory.java82
-rw-r--r--lang/src/main/java/net/openhft/lang/model/Byteable.java34
-rw-r--r--lang/src/main/java/net/openhft/lang/model/ClassModel.java29
-rw-r--r--lang/src/main/java/net/openhft/lang/model/CodeGenerator.java20
-rw-r--r--lang/src/main/java/net/openhft/lang/model/Copyable.java31
-rw-r--r--lang/src/main/java/net/openhft/lang/model/DataValueGenerator.java483
-rw-r--r--lang/src/main/java/net/openhft/lang/model/DataValueMetaModel.java60
-rw-r--r--lang/src/main/java/net/openhft/lang/model/DataValueModel.java37
-rw-r--r--lang/src/main/java/net/openhft/lang/model/DataValueModelImpl.java461
-rw-r--r--lang/src/main/java/net/openhft/lang/model/DataValueModels.java47
-rw-r--r--lang/src/main/java/net/openhft/lang/model/FieldModel.java62
-rw-r--r--lang/src/main/java/net/openhft/lang/model/HeapCodeGenerator.java161
-rw-r--r--lang/src/main/java/net/openhft/lang/model/MethodFilter.java25
-rw-r--r--lang/src/main/java/net/openhft/lang/model/MethodTemplate.java23
-rw-r--r--lang/src/main/java/net/openhft/lang/model/VanillaFilter.java175
-rw-r--r--lang/src/main/java/net/openhft/lang/model/constraints/Digits.java36
-rw-r--r--lang/src/main/java/net/openhft/lang/model/constraints/MaxSize.java34
-rw-r--r--lang/src/main/java/net/openhft/lang/model/constraints/Range.java36
-rwxr-xr-xlang/src/main/java/net/openhft/lang/pool/StringInterner.java78
-rw-r--r--lang/src/main/java/net/openhft/lang/testing/Differencer.java26
-rw-r--r--lang/src/main/java/net/openhft/lang/testing/RunningMinimum.java55
-rw-r--r--lang/src/main/java/net/openhft/lang/testing/VanillaDifferencer.java29
-rw-r--r--lang/src/main/java/net/openhft/lang/thread/NamedThreadFactory.java51
-rw-r--r--lang/src/main/java/net/openhft/lang/values/BooleanValue.java28
-rw-r--r--lang/src/main/java/net/openhft/lang/values/ByteValue.java30
-rw-r--r--lang/src/main/java/net/openhft/lang/values/CharValue.java28
-rw-r--r--lang/src/main/java/net/openhft/lang/values/DoubleValue.java32
-rw-r--r--lang/src/main/java/net/openhft/lang/values/FloatValue.java32
-rw-r--r--lang/src/main/java/net/openhft/lang/values/Int24Value.java33
-rw-r--r--lang/src/main/java/net/openhft/lang/values/Int48Value.java33
-rw-r--r--lang/src/main/java/net/openhft/lang/values/IntValue.java43
-rw-r--r--lang/src/main/java/net/openhft/lang/values/LongValue.java34
-rw-r--r--lang/src/main/java/net/openhft/lang/values/ShortValue.java30
-rw-r--r--lang/src/main/java/net/openhft/lang/values/StringValue.java30
-rw-r--r--lang/src/main/java/net/openhft/lang/values/UnsignedByteValue.java32
-rw-r--r--lang/src/main/java/net/openhft/lang/values/UnsignedIntValue.java33
-rw-r--r--lang/src/main/java/net/openhft/lang/values/UnsignedShortValue.java32
-rw-r--r--lang/src/test/java/net/openhft/lang/JvmTest.java36
-rw-r--r--lang/src/test/java/net/openhft/lang/MathsTest.java53
-rw-r--r--lang/src/test/java/net/openhft/lang/collection/HugeArrayTest.java83
-rw-r--r--lang/src/test/java/net/openhft/lang/collection/HugePricesMain.java90
-rw-r--r--lang/src/test/java/net/openhft/lang/collection/HugeQueueTest.java71
-rw-r--r--lang/src/test/java/net/openhft/lang/io/AllocationRatesTest.java81
-rw-r--r--lang/src/test/java/net/openhft/lang/io/ByteBufferBytesTest.java920
-rw-r--r--lang/src/test/java/net/openhft/lang/io/DirectByteBufferBytesTest.java921
-rwxr-xr-xlang/src/test/java/net/openhft/lang/io/DirectBytesTest.java300
-rw-r--r--lang/src/test/java/net/openhft/lang/io/IOToolsTest.java47
-rw-r--r--lang/src/test/java/net/openhft/lang/io/LockingViaFileLockMain.java94
-rw-r--r--lang/src/test/java/net/openhft/lang/io/LockingViaMMapMain.java97
-rw-r--r--lang/src/test/java/net/openhft/lang/io/MappedFileTest.java97
-rw-r--r--lang/src/test/java/net/openhft/lang/io/MutableDecimalTest.java56
-rw-r--r--lang/src/test/java/net/openhft/lang/io/NativeBytesTest.java926
-rw-r--r--lang/src/test/java/net/openhft/lang/io/examples/ParserExampleMain.java61
-rw-r--r--lang/src/test/java/net/openhft/lang/io/serialization/ByteMarshallableMarshallerTest.java79
-rw-r--r--lang/src/test/java/net/openhft/lang/io/serialization/ExternalizableMarshallerTest.java81
-rw-r--r--lang/src/test/java/net/openhft/lang/io/serialization/RawCopierTest.java93
-rw-r--r--lang/src/test/java/net/openhft/lang/io/serialization/VanillaBytesMarshallerTest.java67
-rw-r--r--lang/src/test/java/net/openhft/lang/io/serialization/direct/DirectSerializationFilterTest.java94
-rw-r--r--lang/src/test/java/net/openhft/lang/io/serialization/direct/DirectSerializationMetadataTest.java78
-rw-r--r--lang/src/test/java/net/openhft/lang/io/serialization/direct/IntrospectTest.java38
-rw-r--r--lang/src/test/java/net/openhft/lang/io/serialization/direct/ObjectMarshallerTest.java245
-rw-r--r--lang/src/test/java/net/openhft/lang/io/serialization/direct/TestClasses.java90
-rw-r--r--lang/src/test/java/net/openhft/lang/model/DataValueGeneratorTest.java174
-rw-r--r--lang/src/test/java/net/openhft/lang/model/DataValueModelTest.java59
-rw-r--r--lang/src/test/java/net/openhft/lang/model/JavaBeanInterface.java70
-rw-r--r--lang/src/test/java/net/openhft/lang/model/MinimalInterface.java58
-rw-r--r--lang/src/test/java/net/openhft/lang/model/NestedA.java39
-rw-r--r--lang/src/test/java/net/openhft/lang/model/NestedArrayInterface.java35
-rw-r--r--lang/src/test/java/net/openhft/lang/model/NestedB.java34
-rw-r--r--lang/src/test/java/net/openhft/lang/model/StringInterface.java34
-rw-r--r--lang/src/test/java/net/openhft/lang/testing/RunningMinimumTest.java50
-rw-r--r--lang/src/test/java/net/openhft/lang/values/CheckValuesBuildTest.java51
-rw-r--r--lang/src/test/java/net/openhft/lang/values/NestAll.java53
-rw-r--r--pom.xml36
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 &amp;#36;{YEAR} Peter Lawrey&#10;&#10;Licensed under the Apache License, Version 2.0 (the &quot;License&quot;);&#10;you may not use this file except in compliance with the License.&#10;You may obtain a copy of the License at&#10;&#10; http://www.apache.org/licenses/LICENSE-2.0&#10; &#10;Unless required by applicable law or agreed to in writing, software&#10;distributed under the License is distributed on an &quot;AS IS&quot; BASIS,&#10;WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.&#10;See the License for the specific language governing permissions and&#10;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 &lt;&lt; 8) | (b &amp; 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 &lt;&lt; 8) | (b &amp; 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 &amp; 0xff) &lt;&lt; 8) | (b &amp; 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 &amp; 0xff) &lt;&lt; 8) | (b &amp; 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 =&gt; Short.MIN_VALUE, Byte.MAX_VALUE =&gt; Short.MAX_VALUE, Byte.MIN_VALUE+2 to
+ * Byte.MAX_VALUE-1 =&gt; same as short value, Byte.MIN_VALUE+1 =&gt; 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 =&gt; readUnsignedShort(), default =&gt; (a &amp; 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 &lt;&lt; 8) | (b &amp; 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 &lt;&lt; 8) | (b &amp; 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 &amp; 0xff) &lt;&lt; 24) | ((b &amp; 0xff) &lt;&lt; 16) | ((c &amp; 0xff) &lt;&lt; 8))) &gt;&gt; 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 &amp; 0xff) &lt;&lt; 24) | ((b &amp; 0xff) &lt;&lt; 16) | ((c &amp; 0xff) &lt;&lt; 8))) &gt;&gt; 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 &amp; 0xff) &lt;&lt; 24) | ((b &amp; 0xff) &lt;&lt; 16) | ((c &amp; 0xff) &lt;&lt; 8) | (d &amp; 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 &amp; 0xff) &lt;&lt; 24) | ((b &amp; 0xff) &lt;&lt; 16) | ((c &amp; 0xff) &lt;&lt; 8) | (d &amp; 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 &amp; 0xff) &lt;&lt; 24) | ((b &amp; 0xff) &lt;&lt; 16) | ((c &amp; 0xff) &lt;&lt; 8) | (d &amp; 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 &amp; 0xff) &lt;&lt; 24) | ((b &amp; 0xff) &lt;&lt; 16) | ((c &amp; 0xff) &lt;&lt; 8) | (d &amp; 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 &amp; 0xff) &lt;&lt; 24) | ((b &amp; 0xff) &lt;&lt; 16) | ((c &amp; 0xff) &lt;&lt; 8) | (d &amp;
+ * 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 &amp; 0xff) &lt;&lt; 24) | ((b &amp; 0xff) &lt;&lt; 16) | ((c &amp; 0xff) &lt;&lt; 8) | (d &amp;
+ * 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 =&gt; Integer.MIN_VALUE, Short.MAX_VALUE =&gt;
+ * Integer.MAX_VALUE, Short.MIN_VALUE+2 to Short.MAX_VALUE-1 =&gt; same as short value, Short.MIN_VALUE+1 =&gt;
+ * 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 =&gt; readUnsignedInt(), default =&gt; (a &amp; 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 &amp; 0xff) &lt;&lt; 56) |
+ * ((long)(b &amp; 0xff) &lt;&lt; 48) |
+ * ((long)(c &amp; 0xff) &lt;&lt; 40) |
+ * ((long)(d &amp; 0xff) &lt;&lt; 32) |
+ * ((long)(e &amp; 0xff) &lt;&lt; 24) |
+ * ((long)(f &amp; 0xff) &lt;&lt; 16) |
+ * ((long)(g &amp; 0xff) &lt;&lt; 8) |
+ * ((long)(h &amp; 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 &amp; 0xff) &lt;&lt; 56) |
+ * ((long)(b &amp; 0xff) &lt;&lt; 48) |
+ * ((long)(c &amp; 0xff) &lt;&lt; 40) |
+ * ((long)(d &amp; 0xff) &lt;&lt; 32) |
+ * ((long)(e &amp; 0xff) &lt;&lt; 24) |
+ * ((long)(f &amp; 0xff) &lt;&lt; 16) |
+ * ((long)(g &amp; 0xff) &lt;&lt; 8) |
+ * ((long)(h &amp; 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 &amp; 0xff) &lt;&lt; 56) |
+ * ((long)(b &amp; 0xff) &lt;&lt; 48) |
+ * ((long)(c &amp; 0xff) &lt;&lt; 40) |
+ * ((long)(d &amp; 0xff) &lt;&lt; 32) |
+ * ((long)(e &amp; 0xff) &lt;&lt; 24) |
+ * ((long)(f &amp; 0xff) &lt;&lt; 16) |
+ * ((long)(g &amp; 0xff) &lt;&lt; 8) |
+ * ((long)(h &amp; 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 &amp; 0xff) &lt;&lt; 56) |
+ * ((long)(b &amp; 0xff) &lt;&lt; 48) |
+ * ((long)(c &amp; 0xff) &lt;&lt; 40) |
+ * ((long)(d &amp; 0xff) &lt;&lt; 32) |
+ * ((long)(e &amp; 0xff) &lt;&lt; 24) |
+ * ((long)(f &amp; 0xff) &lt;&lt; 16) |
+ * ((long)(g &amp; 0xff) &lt;&lt; 8) |
+ * ((long)(h &amp; 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 &amp; 0xff) &lt;&lt; 56) |
+ * ((long)(b &amp; 0xff) &lt;&lt; 48) |
+ * ((long)(c &amp; 0xff) &lt;&lt; 40) |
+ * ((long)(d &amp; 0xff) &lt;&lt; 32) |
+ * ((long)(e &amp; 0xff) &lt;&lt; 24) |
+ * ((long)(f &amp; 0xff) &lt;&lt; 16)) &gt;&gt; 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 &amp; 0xff) &lt;&lt; 56) |
+ * ((long)(b &amp; 0xff) &lt;&lt; 48) |
+ * ((long)(c &amp; 0xff) &lt;&lt; 40) |
+ * ((long)(d &amp; 0xff) &lt;&lt; 32) |
+ * ((long)(e &amp; 0xff) &lt;&lt; 24) |
+ * ((long)(f &amp; 0xff) &lt;&lt; 16)) &gt;&gt; 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 =&gt; Long.MIN_VALUE, Integer.MAX_VALUE =&gt; Long.MAX_VALUE,
+ * Integer.MIN_VALUE+2 to Integer.MAX_VALUE-1 =&gt; same as short value, Integer.MIN_VALUE+1 =&gt; 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()) &lt; 0) {
+ * l |= (b &amp; 0x7FL) &lt;&lt; count;
+ * count += 7;
+ * }
+ * if (b == 0 && count &gt; 0)
+ * return ~l;
+ * return l | (b &lt;&lt; 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 &#32;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>&#92;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&amp; 0x1F) &lt;&lt; 6) | (b &amp; 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 &amp; 0x0F) &lt;&lt; 12) | ((b &amp; 0x3F) &lt;&lt; 6) | (c &amp; 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 &amp; 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 &amp; (v &gt;&gt; 8))
+ * (byte)(0xff &amp; 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 &amp; (v &gt;&gt; 8))
+ * (byte)(0xff &amp; 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 &amp; (v &gt;&gt; 8))
+ * (byte)(0xff &amp; 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 &amp; (v &gt;&gt; 8))
+ * (byte)(0xff &amp; 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 =&gt; Byte.MIN_VALUE, Short.MAX_VALUE =&gt; Byte.MAX_VALUE,
+ * Short.MIN_VALUE+2 to Short.MAX_VALUE-1 =&gt; writeByte(x), default =&gt; 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 =&gt; 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 &amp; 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 &amp; (v &gt;&gt; 8))
+ * (byte)(0xff &amp; 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 &amp; (v &gt;&gt; 8))
+ * (byte)(0xff &amp; 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 &amp; (v &gt;&gt; 16))
+ * (byte)(0xff &amp; (v &gt;&gt; &#32; &#32;8))
+ * (byte)(0xff &amp; 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 &amp; (v &gt;&gt; 16))
+ * (byte)(0xff &amp; (v &gt;&gt; &#32; &#32;8))
+ * (byte)(0xff &amp; 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 &amp; (v &gt;&gt; 24))
+ * (byte)(0xff &amp; (v &gt;&gt; 16))
+ * (byte)(0xff &amp; (v &gt;&gt; &#32; &#32;8))
+ * (byte)(0xff &amp; 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 &amp; (v &gt;&gt; 24))
+ * (byte)(0xff &amp; (v &gt;&gt; 16))
+ * (byte)(0xff &amp; (v &gt;&gt; &#32; &#32;8))
+ * (byte)(0xff &amp; 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 &amp; (v &gt;&gt; 24))
+ * (byte)(0xff &amp; (v &gt;&gt; 16))
+ * (byte)(0xff &amp; (v &gt;&gt; &#32; &#32;8))
+ * (byte)(0xff &amp; 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 &amp;
+ * 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 &amp; (v &gt;&gt; 24))
+ * (byte)(0xff &amp; (v &gt;&gt; 16))
+ * (byte)(0xff &amp; (v &gt;&gt; &#32; &#32;8))
+ * (byte)(0xff &amp; 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 &amp;
+ * 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 =&gt; Short.MIN_VALUE, Integer.MAX_VALUE =&gt;
+ * Short.MAX_VALUE, Short.MIN_VALUE+2 to Short.MAX_VALUE-1 =&gt; writeShort(x), default =&gt;
+ * 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 &lt;&lt; 16) - 2 =&gt; 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 &amp;
+ * 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 &amp; (v &gt;&gt; 40))
+ * (byte)(0xff &amp; (v &gt;&gt; 32))
+ * (byte)(0xff &amp; (v &gt;&gt; 24))
+ * (byte)(0xff &amp; (v &gt;&gt; 16))
+ * (byte)(0xff &amp; (v &gt;&gt; 8))
+ * (byte)(0xff &amp; 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 &amp; ((1L &lt;&lt 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 &amp; (v &gt;&gt; 40))
+ * (byte)(0xff &amp; (v &gt;&gt; 32))
+ * (byte)(0xff &amp; (v &gt;&gt; 24))
+ * (byte)(0xff &amp; (v &gt;&gt; 16))
+ * (byte)(0xff &amp; (v &gt;&gt; 8))
+ * (byte)(0xff &amp; 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 &amp; ((1L &lt;&lt 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 &amp; (v &gt;&gt; 56))
+ * (byte)(0xff &amp; (v &gt;&gt; 48))
+ * (byte)(0xff &amp; (v &gt;&gt; 40))
+ * (byte)(0xff &amp; (v &gt;&gt; 32))
+ * (byte)(0xff &amp; (v &gt;&gt; 24))
+ * (byte)(0xff &amp; (v &gt;&gt; 16))
+ * (byte)(0xff &amp; (v &gt;&gt; 8))
+ * (byte)(0xff &amp; 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 &amp; (v &gt;&gt; 56))
+ * (byte)(0xff &amp; (v &gt;&gt; 48))
+ * (byte)(0xff &amp; (v &gt;&gt; 40))
+ * (byte)(0xff &amp; (v &gt;&gt; 32))
+ * (byte)(0xff &amp; (v &gt;&gt; 24))
+ * (byte)(0xff &amp; (v &gt;&gt; 16))
+ * (byte)(0xff &amp; (v &gt;&gt; 8))
+ * (byte)(0xff &amp; 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 =&gt; Integer.MIN_VALUE, Long.MAX_VALUE =&gt;
+ * Integer.MAX_VALUE, Integer.MIN_VALUE+2 to Integer.MAX_VALUE-1 =&gt; writeInt(x), default =&gt;
+ * 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>&#92;u0001</code> through
+ * <code>&#92;u007f</code>, it is represented by one byte:<p>
+ * <pre>(byte)c </pre> <p>
+ * If a character <code>c</code> is <code>&#92;u0000</code> or is in the range <code>&#92;u0080</code> through
+ * <code>&#92;u07ff</code>, then it is represented by two bytes, to be written
+ * in the order shown:<p> <pre><code>
+ * (byte)(0xc0 | (0x1f &amp; (c &gt;&gt; 6)))
+ * (byte)(0x80 | (0x3f &amp; c))
+ * </code></pre> <p> If a character
+ * <code>c</code> is in the range <code>&#92;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 &amp; (c &gt;&gt; 12)))
+ * (byte)(0x80 | (0x3f &amp; (c &gt;&gt; 6)))
+ * (byte)(0x80 | (0x3f &amp; 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();
+
+}
diff --git a/pom.xml b/pom.xml
new file mode 100644
index 0000000..d6cd748
--- /dev/null
+++ b/pom.xml
@@ -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