diff options
author | Emmanuel Bourg <ebourg@apache.org> | 2016-08-03 10:17:09 +0200 |
---|---|---|
committer | Emmanuel Bourg <ebourg@apache.org> | 2016-08-03 10:17:09 +0200 |
commit | 3d9b009255ffd897a9bd84ca3977cd3f553da8ef (patch) | |
tree | 13a8296e2989ff8da3fc50d335fd48b2dd0c6064 /lang-sandbox | |
parent | b2ec1a2d459cfef3ff13133c1f7f5972e3740258 (diff) |
Imported Upstream version 6.7.6
Diffstat (limited to 'lang-sandbox')
30 files changed, 2721 insertions, 0 deletions
diff --git a/lang-sandbox/pom.xml b/lang-sandbox/pom.xml new file mode 100755 index 0000000..a293f4d --- /dev/null +++ b/lang-sandbox/pom.xml @@ -0,0 +1,96 @@ +<?xml version="1.0" encoding="UTF-8"?> +<!-- + ~ Copyright (C) 2015 higherfrequencytrading.com + ~ + ~ This program is free software: you can redistribute it and/or modify + ~ it under the terms of the GNU Lesser General Public License as published by + ~ the Free Software Foundation, either version 3 of the License. + ~ + ~ This program is distributed in the hope that it will be useful, + ~ but WITHOUT ANY WARRANTY; without even the implied warranty of + ~ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + ~ GNU Lesser General Public License for more details. + ~ + ~ You should have received a copy of the GNU Lesser General Public License + ~ along with this program. If not, see <http://www.gnu.org/licenses/>. + --> + +<project xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" + xmlns="http://maven.apache.org/POM/4.0.0" + xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd"> + + <parent> + <groupId>net.openhft</groupId> + <artifactId>java-parent-pom</artifactId> + <version>1.1.1</version> + <relativePath/> + </parent> + + <name>OpenHFT/Java-Lang/lang-sandbox</name> + <modelVersion>4.0.0</modelVersion> + <artifactId>lang-sandbox</artifactId> + <version>6.7.1-SNAPSHOT</version> + + <dependencyManagement> + <dependencies> + <dependency> + <groupId>net.openhft</groupId> + <artifactId>third-party-bom</artifactId> + <type>pom</type> + <version>3.4.20</version> + <scope>import</scope> + </dependency> + <dependency> + <groupId>net.openhft</groupId> + <artifactId>lang</artifactId> + <version>${project.version}</version> + </dependency> + </dependencies> + </dependencyManagement> + + <dependencies> + + <dependency> + <groupId>org.slf4j</groupId> + <artifactId>slf4j-api</artifactId> + </dependency> + + <dependency> + <groupId>net.openhft</groupId> + <artifactId>lang</artifactId> + </dependency> + + <dependency> + <groupId>com.sun.java</groupId> + <artifactId>tools</artifactId> + </dependency> + + <dependency> + <groupId>junit</groupId> + <artifactId>junit</artifactId> + <scope>test</scope> + </dependency> + + <dependency> + <groupId>org.slf4j</groupId> + <artifactId>slf4j-simple</artifactId> + <scope>test</scope> + </dependency> + + </dependencies> + <build> + <plugins> + <plugin> + <groupId>org.apache.maven.plugins</groupId> + <artifactId>maven-compiler-plugin</artifactId> + <configuration> + <compilerArgument>-Xlint:deprecation</compilerArgument> + <source>1.8</source> + <target>1.8</target> + <encoding>UTF-8</encoding> + </configuration> + </plugin> + </plugins> + </build> + +</project> diff --git a/lang-sandbox/src/main/java/net/openhft/lang/WaitStrategy.java b/lang-sandbox/src/main/java/net/openhft/lang/WaitStrategy.java new file mode 100644 index 0000000..b53151c --- /dev/null +++ b/lang-sandbox/src/main/java/net/openhft/lang/WaitStrategy.java @@ -0,0 +1,25 @@ +/* + * Copyright 2014 Higher Frequency Trading + * + * http://www.higherfrequencytrading.com + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package net.openhft.lang; + +/* + * JLANGX-10 Wait strategy + */ +public interface WaitStrategy { + public void await(int counter); +} diff --git a/lang-sandbox/src/main/java/net/openhft/lang/WaitStrategyBuilder.java b/lang-sandbox/src/main/java/net/openhft/lang/WaitStrategyBuilder.java new file mode 100644 index 0000000..d92e75a --- /dev/null +++ b/lang-sandbox/src/main/java/net/openhft/lang/WaitStrategyBuilder.java @@ -0,0 +1,190 @@ +/* + * Copyright 2014 Higher Frequency Trading + * + * http://www.higherfrequencytrading.com + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package net.openhft.lang; + +import java.util.LinkedList; +import java.util.List; + +public class WaitStrategyBuilder { + private int lowerLimit; + private List<WaiterHelper> waiters; + + public WaitStrategyBuilder() { + this.waiters = new LinkedList<WaiterHelper>(); + this.lowerLimit = 0; + } + + // ************************************************************************* + // + // ************************************************************************* + + public WaitStrategyBuilder busyWaiter(int count) { + return waiter(Waiters.BUSY_WAITER,count); + } + + public WaitStrategyBuilder yeldWaiter(int count) { + return waiter(Waiters.YELD_WAITER,count); + } + + public WaitStrategyBuilder parkWaiter(int count) { + return waiter(Waiters.PARK_WAITER,count); + } + + public WaitStrategyBuilder waiter(Waiter waiter,int count) { + this.waiters.add(new WaiterHelper(waiter,this.lowerLimit,count)); + this.lowerLimit += count; + return this; + } + + public WaitStrategy build() { + switch(this.waiters.size()) { + case 1: + return new WaitStrategy1(this.waiters); + case 2: + return new WaitStrategy2(this.waiters); + case 3: + return new WaitStrategy3(this.waiters); + } + + return new WaitStrategyN(this.waiters); + } + + // ************************************************************************* + // + // ************************************************************************* + + private class WaiterHelper { + private final Waiter waiter; + private int min; + private int max; + + public WaiterHelper(final Waiter waiter) { + this(waiter,-1,-1); + } + + public WaiterHelper(final Waiter waiter,int start, int count) { + this.waiter = waiter; + this.min = start; + this.max = start + count; + } + + public boolean contains(int value) { + return value >= this.min && value <= this.min; + } + + public Waiter waiter() { + return this.waiter; + } + + public int min() { + return this.min; + } + + public int max() { + return this.max; + } + } + + // ************************************************************************* + // + // ************************************************************************* + + private class WaitStrategy1 implements WaitStrategy { + private final Waiter waiter; + + public WaitStrategy1(final List<WaiterHelper> waiters) { + this.waiter = waiters.get(0).waiter; + } + + @Override + public void await(int counter) { + this.waiter.await(); + } + } + + private class WaitStrategy2 implements WaitStrategy { + private final int limit; + private final Waiter waiter1; + private final Waiter waiter2; + + public WaitStrategy2(final List<WaiterHelper> waiters) { + this.limit = waiters.get(0).max; + this.waiter1 = waiters.get(0).waiter; + this.waiter2 = waiters.get(1).waiter; + } + + @Override + public void await(int counter) { + if(counter < this.limit) { + this.waiter1.await(); + + } else { + this.waiter2.await(); + } + } + } + + private class WaitStrategy3 implements WaitStrategy { + private final int limit1; + private final int limit2; + private final Waiter waiter1; + private final Waiter waiter2; + private final Waiter waiter3; + + public WaitStrategy3(final List<WaiterHelper> waiters) { + this.limit1 = waiters.get(0).max; + this.limit2 = waiters.get(1).max; + this.waiter1 = waiters.get(0).waiter; + this.waiter2 = waiters.get(1).waiter; + this.waiter3 = waiters.get(2).waiter; + } + + @Override + public void await(int counter) { + if(counter < this.limit1) { + this.waiter1.await(); + + } else if(counter < this.limit2) { + this.waiter2.await(); + + } else { + this.waiter3.await(); + } + } + } + + private class WaitStrategyN implements WaitStrategy { + final List<WaiterHelper> waiters; + + public WaitStrategyN(final List<WaiterHelper> waiters) { + this.waiters = new LinkedList<WaiterHelper>(waiters); + } + + @Override + public void await(int counter) { + WaiterHelper wh = null; + for(int i=0;i<this.waiters.size();i++) { + wh = this.waiters.get(i); + if(i == this.waiters.size() - 1 || wh.contains(counter)) { + wh.waiter().await(); + break; + } + } + } + } +} diff --git a/lang-sandbox/src/main/java/net/openhft/lang/Waiter.java b/lang-sandbox/src/main/java/net/openhft/lang/Waiter.java new file mode 100644 index 0000000..0275376 --- /dev/null +++ b/lang-sandbox/src/main/java/net/openhft/lang/Waiter.java @@ -0,0 +1,22 @@ +/* + * Copyright 2014 Higher Frequency Trading + * + * http://www.higherfrequencytrading.com + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package net.openhft.lang; + +public interface Waiter { + public void await(); +} diff --git a/lang-sandbox/src/main/java/net/openhft/lang/Waiters.java b/lang-sandbox/src/main/java/net/openhft/lang/Waiters.java new file mode 100644 index 0000000..4dd73e2 --- /dev/null +++ b/lang-sandbox/src/main/java/net/openhft/lang/Waiters.java @@ -0,0 +1,46 @@ +/* + * Copyright 2014 Higher Frequency Trading + * + * http://www.higherfrequencytrading.com + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package net.openhft.lang; + +import java.util.concurrent.locks.LockSupport; + +public class Waiters { + public static final Waiter BUSY_WAITER = new BusyWaiter(); + public static final Waiter YELD_WAITER = new YieldWaiter(); + public static final Waiter PARK_WAITER = new ParkWaiter(); + + public static final class BusyWaiter implements Waiter { + @Override + public void await() { + } + } + + public static final class YieldWaiter implements Waiter { + @Override + public void await() { + Thread.yield(); + } + } + + public static final class ParkWaiter implements Waiter { + @Override + public void await() { + LockSupport.parkNanos(1L); + } + } +} diff --git a/lang-sandbox/src/main/java/net/openhft/lang/arena/Arena.java b/lang-sandbox/src/main/java/net/openhft/lang/arena/Arena.java new file mode 100755 index 0000000..9b76bc4 --- /dev/null +++ b/lang-sandbox/src/main/java/net/openhft/lang/arena/Arena.java @@ -0,0 +1,74 @@ +/* + * Copyright 2014 Higher Frequency Trading + * + * http://www.higherfrequencytrading.com + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package net.openhft.lang.arena; + +import net.openhft.lang.io.Bytes; +import net.openhft.lang.io.NativeBytes; +import net.openhft.lang.io.OffHeapLock; + +/** + * Created by peter.lawrey on 20/06/14. + */ +public interface Arena { + enum Mode { + SINGLE_BLOCK, CONTINUOUS_BLOCKS, LINKED_BLOCKS + } + + Mode getMode(); + + OffHeapLock readLock(); + + OffHeapLock writeLock(); + + // log2(allocation multiple) + int blockSizeBits(); + + // allocate a block of memory + int /* handle */ allocate(int hash, int sizeInBlocks); + + // resize a block of memory + int /* new handle */ reallocate(int hash, int handle, int sizeInBlocks); + + // free a block of memory + void free(int hash, int handle, int sizeInBlocks); + + // read to data stored. + void copyTo(Bytes bytes, int handle); + + // read from data stored + void copyFrom(int handle, Bytes bytes); + + // map the data directly. + void setBytes(int handle, NativeBytes bytes) throws IllegalStateException; + + // start a hash lookup + int firstHandleFor(int hash); + + // next hash or 0 + int nextHandle(); + + // metrics + int handlesUsed(); + + int handlesCapacity(); + + int entriesUsed(); + + int entriesCapacity(); +} diff --git a/lang-sandbox/src/main/java/net/openhft/lang/arena/MappedArena.java b/lang-sandbox/src/main/java/net/openhft/lang/arena/MappedArena.java new file mode 100755 index 0000000..cbd3223 --- /dev/null +++ b/lang-sandbox/src/main/java/net/openhft/lang/arena/MappedArena.java @@ -0,0 +1,134 @@ +/* + * Copyright 2014 Higher Frequency Trading + * + * http://www.higherfrequencytrading.com + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package net.openhft.lang.arena; + +import net.openhft.lang.io.*; +import net.openhft.lang.io.serialization.ObjectSerializer; + +import java.io.File; +import java.io.IOException; +import java.nio.channels.FileChannel; + +/** + * Created by peter.lawrey on 22/06/14. + */ +public class MappedArena implements Arena { + static final byte[] MAGIC = "Arena001".getBytes(); + static final int MAGIC_OFFSET = 0; + static final int LOCK_OFFSET = MAGIC_OFFSET + 8; + static final int ALLOCATE_SIZE_OFFSET = LOCK_OFFSET + 8; + static final int ALLOCATIONS_OFFSET = ALLOCATE_SIZE_OFFSET + 4; + static final int HEADER_ID_OFFSET0 = 32; + static final int HEADER_ID_SIZE = 4; + static final int HEADER_LENGTH = 64; + + final MappedArenaStores stores; + private final Mode mode; + private final OffHeapLock readLock; + private final OffHeapLock writeLock; + private final DirectBytes header; + + /** + * The lock consists of the reading/writing mode, writer count, writers waiting, reader count, readers waiting + */ + public MappedArena(File file, long minSize, ObjectSerializer objectSerializer, Mode mode) throws IOException { + this.mode = mode; + stores = new MappedArenaStores(file, FileChannel.MapMode.READ_WRITE, minSize, objectSerializer); + header = stores.acquire(0, HEADER_LENGTH); + OffHeapReadWriteLock ohrwl = new OffHeapReadWriteLock(header, LOCK_OFFSET); + readLock = ohrwl.readLock(); + writeLock = ohrwl.writeLock(); + } + + @Override + public Mode getMode() { + return mode; + } + + @Override + public OffHeapLock readLock() { + return readLock; + } + + @Override + public OffHeapLock writeLock() { + return writeLock; + } + + @Override + public int blockSizeBits() { + return 0; + } + + @Override + public int allocate(int hash, int sizeInBlocks) { + return 0; + } + + @Override + public int reallocate(int hash, int handle, int sizeInBlocks) { + return 0; + } + + @Override + public void free(int hash, int handle, int sizeInBlocks) { + } + + @Override + public void copyTo(Bytes bytes, int handle) { + } + + @Override + public void copyFrom(int handle, Bytes bytes) { + } + + @Override + public void setBytes(int handle, NativeBytes bytes) throws IllegalStateException { + } + + @Override + public int firstHandleFor(int hash) { + return 0; + } + + @Override + public int nextHandle() { + return 0; + } + + @Override + public int handlesUsed() { + return 0; + } + + @Override + public int handlesCapacity() { + return 0; + } + + @Override + public int entriesUsed() { + return 0; + } + + @Override + public int entriesCapacity() { + return 0; + } +} diff --git a/lang-sandbox/src/main/java/net/openhft/lang/arena/MappedArenaStores.java b/lang-sandbox/src/main/java/net/openhft/lang/arena/MappedArenaStores.java new file mode 100644 index 0000000..7918aa0 --- /dev/null +++ b/lang-sandbox/src/main/java/net/openhft/lang/arena/MappedArenaStores.java @@ -0,0 +1,225 @@ +/* + * Copyright 2014 Higher Frequency Trading + * + * http://www.higherfrequencytrading.com + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package net.openhft.lang.arena; + +import net.openhft.lang.io.BytesStore; +import net.openhft.lang.io.DirectBytes; +import net.openhft.lang.io.serialization.ObjectSerializer; +import net.openhft.lang.model.constraints.NotNull; +import sun.misc.Cleaner; +import sun.nio.ch.FileChannelImpl; + +import java.io.Closeable; +import java.io.File; +import java.io.IOException; +import java.io.RandomAccessFile; +import java.lang.reflect.InvocationTargetException; +import java.lang.reflect.Method; +import java.nio.channels.FileChannel; +import java.util.ArrayList; +import java.util.List; +import java.util.concurrent.atomic.AtomicInteger; + +public class MappedArenaStores implements Closeable { + + private static final int MAP_RO = 0; + private static final int MAP_RW = 1; + private static final int MAP_PV = 2; + + // retain to prevent GC. + private final File file; + private final RandomAccessFile randomAccessFile; + private final FileChannel fileChannel; + private final FileChannel.MapMode mode; + private final List<MappedArenaStore> storeList = new ArrayList<MappedArenaStore>(); + private final ObjectSerializer objectSerializer; + + public MappedArenaStores(File file, FileChannel.MapMode mode, long minSize, ObjectSerializer objectSerializer) throws IOException { + if (minSize < 0 || minSize > 128L << 40) { + throw new IllegalArgumentException("invalid minSize: " + minSize); + } + + this.file = file; + this.objectSerializer = objectSerializer; + + try { + randomAccessFile = new RandomAccessFile(file, accessModeFor(mode)); + this.mode = mode; + this.fileChannel = randomAccessFile.getChannel(); + storeList.add(new MappedArenaStore(0, minSize)); + } catch (Exception e) { + throw wrap(e); + } + } + + public DirectBytes acquire(long offset, long size) throws IOException { + MappedArenaStore mas = acquireMAS(offset, size); + return mas.bytes(offset - mas.offset, size); + } + + private MappedArenaStore acquireMAS(long offset, long size) throws IOException { + long end = offset + size; + for (MappedArenaStore store : storeList) { + if (store.offset >= offset && store.end <= end) + return store; + } + try { + MappedArenaStore store = new MappedArenaStore(offset, size); + storeList.add(store); + return store; + } catch (Exception e) { + throw wrap(e); + } + } + + @Override + public void close() throws IOException { + fileChannel.close(); + for (MappedArenaStore store : storeList) { + store.free(); + } + } + + class MappedArenaStore implements BytesStore { + private final long address; + private final Cleaner cleaner; + private final AtomicInteger refCount = new AtomicInteger(1); + final long offset, size, end; + + MappedArenaStore(long offset, long size) throws NoSuchMethodException, IllegalAccessException, InvocationTargetException, IOException { + this.offset = offset; + this.size = size; + this.end = offset + size; + if (randomAccessFile.length() < end) { + if (mode != FileChannel.MapMode.READ_WRITE) { + throw new IOException("Cannot resize file to " + end + " as mode is not READ_WRITE"); + } + + randomAccessFile.setLength(end); + } + this.address = map0(fileChannel, imodeFor(mode), offset, size); + this.cleaner = Cleaner.create(this, new Unmapper(address, size, fileChannel)); + } + + @Override + public ObjectSerializer objectSerializer() { + return objectSerializer; + } + + @Override + public long address() { + return address; + } + + @Override + public long size() { + return size; + } + + @Override + public void free() { + cleaner.clean(); + } + + @NotNull + public DirectBytes bytes() { + return new DirectBytes(this, refCount); + } + + @NotNull + public DirectBytes bytes(long offset, long length) { + return new DirectBytes(this, refCount, offset, length); + } + + @Override + public File file() { + return file; + } + } + + private static long map0(FileChannel fileChannel, int imode, long start, long size) throws NoSuchMethodException, IllegalAccessException, InvocationTargetException { + Method map0 = fileChannel.getClass().getDeclaredMethod("map0", int.class, long.class, long.class); + map0.setAccessible(true); + return (Long) map0.invoke(fileChannel, imode, start, size); + } + + private static void unmap0(long address, long size) throws IOException { + try { + Method unmap0 = FileChannelImpl.class.getDeclaredMethod("unmap0", long.class, long.class); + unmap0.setAccessible(true); + unmap0.invoke(null, address, size); + } catch (Exception e) { + throw wrap(e); + } + } + + private static IOException wrap(Throwable e) { + if (e instanceof InvocationTargetException) + e = e.getCause(); + if (e instanceof IOException) + return (IOException) e; + return new IOException(e); + } + + private static String accessModeFor(FileChannel.MapMode mode) { + return mode == FileChannel.MapMode.READ_WRITE ? "rw" : "r"; + } + + private static int imodeFor(FileChannel.MapMode mode) { + int imode = -1; + if (mode == FileChannel.MapMode.READ_ONLY) + imode = MAP_RO; + else if (mode == FileChannel.MapMode.READ_WRITE) + imode = MAP_RW; + else if (mode == FileChannel.MapMode.PRIVATE) + imode = MAP_PV; + assert (imode >= 0); + return imode; + } + + static class Unmapper implements Runnable { + private final long size; + private final FileChannel channel; + private volatile long address; + + Unmapper(long address, long size, FileChannel channel) { + assert (address != 0); + this.address = address; + this.size = size; + this.channel = channel; + } + + public void run() { + if (address == 0) + return; + + try { + unmap0(address, size); + address = 0; + + if (channel.isOpen()) { + channel.close(); + } + } catch (IOException e) { + e.printStackTrace(); + } + } + } +} + diff --git a/lang-sandbox/src/main/java/net/openhft/lang/data-types.txt b/lang-sandbox/src/main/java/net/openhft/lang/data-types.txt new file mode 100755 index 0000000..c3a68cb --- /dev/null +++ b/lang-sandbox/src/main/java/net/openhft/lang/data-types.txt @@ -0,0 +1,19 @@ +sequence_push +sequence_pop +group_push +group_pop +mapping_start {data} +mapping_end +named_mapping {text} {data} +numbered_mapping {number} {data} +comment {data} +document_start {text-type} +document_end +date {date-type} x +type {text-type} +define_alias {text-ref} {data} +alias {text-ref} + +need to recognise +- nested sequences +- nested mappings diff --git a/lang-sandbox/src/main/java/net/openhft/lang/io/OffHeapLock.java b/lang-sandbox/src/main/java/net/openhft/lang/io/OffHeapLock.java new file mode 100755 index 0000000..c980619 --- /dev/null +++ b/lang-sandbox/src/main/java/net/openhft/lang/io/OffHeapLock.java @@ -0,0 +1,34 @@ +/* + * Copyright 2014 Higher Frequency Trading + * + * http://www.higherfrequencytrading.com + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package net.openhft.lang.io; + +import java.util.concurrent.TimeUnit; + +/** +* Created by peter.lawrey on 03/08/14. +*/ +public interface OffHeapLock { + boolean tryLock(); + + void busyLock(); + + boolean busyLock(long time, TimeUnit timeUnit); + + void unlock(); +} diff --git a/lang-sandbox/src/main/java/net/openhft/lang/io/OffHeapReadWriteLock.java b/lang-sandbox/src/main/java/net/openhft/lang/io/OffHeapReadWriteLock.java new file mode 100755 index 0000000..4324956 --- /dev/null +++ b/lang-sandbox/src/main/java/net/openhft/lang/io/OffHeapReadWriteLock.java @@ -0,0 +1,191 @@ +/* + * Copyright 2014 Higher Frequency Trading + * + * http://www.higherfrequencytrading.com + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package net.openhft.lang.io; + +import java.util.concurrent.TimeUnit; + +/** + * Created by peter.lawrey on 03/08/14. + */ +public class OffHeapReadWriteLock { + private final Bytes bytes; + private final long offset; + private final ReadLock readLock; + private final WriteLock writeLock; + + public OffHeapReadWriteLock(Bytes bytes, long offset) { + this.bytes = bytes; + this.offset = offset; + this.readLock = new ReadLock(); + this.writeLock = new WriteLock(); + } + + long getLock() { + return bytes.readVolatileLong(offset); + } + + boolean trySetLock(long lock0, long lock2) { + return bytes.compareAndSwapLong(offset, lock0, lock2); + } + + static final String[] RW_MODES = {"none", "read", "write"}; + + public ReadLock readLock() { + return readLock; + } + + public WriteLock writeLock() { + return writeLock; + } + class ReadLock implements OffHeapLock { + @Override + public boolean tryLock() { + long lock = getLock(); + int rwMode = (int) (lock >>> 56) & 0x3; + if (rwMode < 2) { + long lock2 = (lock | (1L << 56)) + (1 << 16); + return trySetLock(lock, lock2); + } + return false; + } + + @Override + public void busyLock() { + busyLock(Long.MAX_VALUE, TimeUnit.NANOSECONDS); + } + + @Override + public boolean busyLock(long time, TimeUnit timeUnit) { + if (tryLock()) return true; + addWaitingReader(+1); + try { + long end = System.nanoTime() + timeUnit.convert(time, TimeUnit.NANOSECONDS); + do { + if (tryLock()) return true; + Thread.yield(); + } while (System.nanoTime() < end); + } finally { + addWaitingReader(-1); + } + return false; + } + + void addWaitingReader(int add) { + for(;;) { + long lock = getLock(); + int readerWaiting = (int) lock & 0xFFFF; + int readerWaiting2 = readerWaiting + add; + if (readerWaiting2 >= 1 << 16 || readerWaiting2 < 0) + // todo error state + return; + if (trySetLock(lock, (lock & ~0xFFFF) + readerWaiting2)) + return; + } + } + + @Override + public void unlock() { + for (; ; ) { + long lock = getLock(); + long lock2 = lock - (1 << 16); + if (trySetLock(lock, nextMode(lock2))) + return; + } + } + } + + public String toString() { + long lock = getLock(); + int rwMode = (int) (lock >>> 56) & 0x3; + int writerCount = (int) (lock >>> 48) & 0xFF; + int writerWaiting = (int) (lock >>> 32) & 0xFFFF; + int readerCount = (int) (lock >>> 16) & 0xFFFF; + int readerWaiting = (int) lock & 0xFFFF; + return RW_MODES[rwMode] + " w:" + writerCount + "/" + writerWaiting + " r:" + readerCount + "/" + readerWaiting; + } + + private long nextMode(long lock) { + int rwMode = (int) (lock >>> 56) & 0x3; +// int writerCount = (int) (lock >>> 48) & 0xFF; + int writerWaiting = (int) (lock >>> 32) & 0xFFFF; + int readerCount = (int) (lock >>> 16) & 0xFFFF; +// int readerWaiting = (int) lock & 0xFFFF; + if ((rwMode == 1 && readerCount == 0) || (rwMode == 2 && writerWaiting == 0)) + return lock & (~0L >>> 8); // clear mode. + return lock; + } + + class WriteLock implements OffHeapLock{ + @Override + public boolean tryLock() { + long lock = getLock(); + int rwMode = (int) (lock >>> 56) & 0x3; + int writerCount = (int) (lock >>> 48) & 0xFF; + if ((rwMode & 1) == 0 && ( writerCount == 0)) { + long lock2 = (lock | (2L << 56)) + (1L << 48); + return trySetLock(lock, lock2); + } + return false; + } + + @Override + public void busyLock() { + busyLock(Long.MAX_VALUE, TimeUnit.NANOSECONDS); + } + + @Override + public boolean busyLock(long time, TimeUnit timeUnit) { + if (tryLock()) return true; + addWaitingWriter(+1); + try { + long end = System.nanoTime() + timeUnit.convert(time, TimeUnit.NANOSECONDS); + do { + if (tryLock()) return true; + Thread.yield(); + } while (System.nanoTime() < end); + } finally { + addWaitingWriter(-1); + } + return false; + } + + void addWaitingWriter(int add) { + for(;;) { + long lock = getLock(); + int writerWaiting = (int) (lock >>> 32) & 0xFFFF; + int writerWaiting2 = writerWaiting + add; + if (writerWaiting2 >= 1 << 16 || writerWaiting2 < 0) + // todo error state + return; + if (trySetLock(lock, (lock & ~0xFFFF00000000L) + ((long) writerWaiting2 << 32))) + return; + } + } + + @Override + public void unlock() { + for (; ; ) { + long lock = getLock(); + long lock2 = lock - (1L << 48); + if (trySetLock(lock, nextMode(lock2))) + return; + } + } + } +} diff --git a/lang-sandbox/src/main/java/net/openhft/lang/io/serialization/RawCopier.java b/lang-sandbox/src/main/java/net/openhft/lang/io/serialization/RawCopier.java new file mode 100644 index 0000000..b5e224e --- /dev/null +++ b/lang-sandbox/src/main/java/net/openhft/lang/io/serialization/RawCopier.java @@ -0,0 +1,109 @@ +/* + * Copyright 2014 Higher Frequency Trading + * + * http://www.higherfrequencytrading.com + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package net.openhft.lang.io.serialization; + +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; + final int end; + private final Class<T> tClass; + + private 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 static void addAllFields(List<Field> fields, Class tClass) { + if (tClass != null && tClass != Object.class) + addAllFields(fields, tClass.getSuperclass()); + if (tClass != null) { + for (Field field : tClass.getDeclaredFields()) { + if (!Modifier.isStatic(field.getModifiers())) + fields.add(field); + } + } + } +} diff --git a/lang-sandbox/src/main/java/net/openhft/lang/io/serialization/direct/DirectSerializationFilter.java b/lang-sandbox/src/main/java/net/openhft/lang/io/serialization/direct/DirectSerializationFilter.java new file mode 100644 index 0000000..4513448 --- /dev/null +++ b/lang-sandbox/src/main/java/net/openhft/lang/io/serialization/direct/DirectSerializationFilter.java @@ -0,0 +1,50 @@ +/* + * Copyright 2014 Higher Frequency Trading + * + * http://www.higherfrequencytrading.com + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package net.openhft.lang.io.serialization.direct; + +import java.lang.reflect.Field; +import java.util.ArrayList; +import java.util.Collections; +import java.util.List; + +import static net.openhft.lang.io.serialization.direct.FieldMetadata.*; + +final class DirectSerializationFilter { + public static List<Field> stopAtFirstIneligibleField(List<Field> fields) { + ArrayList<Field> eligibleFields = new ArrayList<Field>(); + for (Field f : fields) { + if (checkEligible(f)) { + eligibleFields.add(f); + + } else { + break; + } + } + + return eligibleFields.isEmpty() ? + Collections.<Field>emptyList() : + eligibleFields; + } + + private static boolean checkEligible(Field f) { + return isPrimitive(f) && + !isStatic(f) && + !isTransient(f); + } +}
\ No newline at end of file diff --git a/lang-sandbox/src/main/java/net/openhft/lang/io/serialization/direct/DirectSerializationMetadata.java b/lang-sandbox/src/main/java/net/openhft/lang/io/serialization/direct/DirectSerializationMetadata.java new file mode 100644 index 0000000..b18e521 --- /dev/null +++ b/lang-sandbox/src/main/java/net/openhft/lang/io/serialization/direct/DirectSerializationMetadata.java @@ -0,0 +1,107 @@ +/* + * Copyright 2014 Higher Frequency Trading + * + * http://www.higherfrequencytrading.com + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package net.openhft.lang.io.serialization.direct; + +import net.openhft.lang.Jvm; + +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; + private static final long OOP_SIZE = UNSAFE.arrayIndexScale(Object[].class); + static final long OBJECT_HEADER_SIZE = NATIVE_WORD_SIZE + OOP_SIZE; // Object header has a native sized mark word + variable sized oop to klass meta object + + private 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); + } + + private 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-sandbox/src/main/java/net/openhft/lang/io/serialization/direct/FieldMetadata.java b/lang-sandbox/src/main/java/net/openhft/lang/io/serialization/direct/FieldMetadata.java new file mode 100644 index 0000000..00f96c5 --- /dev/null +++ b/lang-sandbox/src/main/java/net/openhft/lang/io/serialization/direct/FieldMetadata.java @@ -0,0 +1,41 @@ +/* + * Copyright 2014 Higher Frequency Trading + * + * http://www.higherfrequencytrading.com + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package net.openhft.lang.io.serialization.direct; + +import java.lang.reflect.Field; +import java.lang.reflect.Modifier; + +final class FieldMetadata { + public static boolean isPrimitive(Field f) { + return f.getType().isPrimitive(); + } + + public static boolean isPrimitiveArray(Field f) { + Class<?> clazz = f.getType(); + return clazz.isArray() && clazz.getComponentType().isPrimitive(); + } + + public static boolean isStatic(Field f) { + return Modifier.isStatic(f.getModifiers()); + } + + public static boolean isTransient(Field f) { + return Modifier.isTransient(f.getModifiers()); + } +} diff --git a/lang-sandbox/src/main/java/net/openhft/lang/io/serialization/direct/Introspect.java b/lang-sandbox/src/main/java/net/openhft/lang/io/serialization/direct/Introspect.java new file mode 100644 index 0000000..5152571 --- /dev/null +++ b/lang-sandbox/src/main/java/net/openhft/lang/io/serialization/direct/Introspect.java @@ -0,0 +1,65 @@ +/* + * Copyright 2014 Higher Frequency Trading + * + * http://www.higherfrequencytrading.com + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package net.openhft.lang.io.serialization.direct; + +import net.openhft.lang.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-sandbox/src/main/java/net/openhft/lang/io/serialization/direct/ObjectMarshaller.java b/lang-sandbox/src/main/java/net/openhft/lang/io/serialization/direct/ObjectMarshaller.java new file mode 100644 index 0000000..2520634 --- /dev/null +++ b/lang-sandbox/src/main/java/net/openhft/lang/io/serialization/direct/ObjectMarshaller.java @@ -0,0 +1,68 @@ +/* + * Copyright 2014 Higher Frequency Trading + * + * http://www.higherfrequencytrading.com + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package net.openhft.lang.io.serialization.direct; + +import net.openhft.lang.io.Bytes; + +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-sandbox/src/main/java/net/openhft/lang/io/serialization/direct/ObjectMarshallers.java b/lang-sandbox/src/main/java/net/openhft/lang/io/serialization/direct/ObjectMarshallers.java new file mode 100644 index 0000000..d0f0a03 --- /dev/null +++ b/lang-sandbox/src/main/java/net/openhft/lang/io/serialization/direct/ObjectMarshallers.java @@ -0,0 +1,94 @@ +/* + * Copyright 2014 Higher Frequency Trading + * + * http://www.higherfrequencytrading.com + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package net.openhft.lang.io.serialization.direct; + +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +import java.lang.reflect.Field; +import java.util.Collection; +import java.util.HashMap; +import java.util.List; +import java.util.Map; + +import static net.openhft.lang.io.serialization.direct.DirectSerializationMetadata.SerializationMetadata; + +final class ObjectMarshallers { + private static final Logger Log = LoggerFactory.getLogger(ObjectMarshallers.class); + + 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.warn("Class {} has metadata {}", 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.warn( + "The following fields in Class {} will not be copied by ObjectMarshaller:\n{}", + 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-sandbox/src/main/java/net/openhft/lang/model/ClassModel.java b/lang-sandbox/src/main/java/net/openhft/lang/model/ClassModel.java new file mode 100644 index 0000000..2777560 --- /dev/null +++ b/lang-sandbox/src/main/java/net/openhft/lang/model/ClassModel.java @@ -0,0 +1,31 @@ +/* + * Copyright 2014 Higher Frequency Trading + * + * http://www.higherfrequencytrading.com + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package net.openhft.lang.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-sandbox/src/main/java/net/openhft/lang/model/DataValueMetaModel.java b/lang-sandbox/src/main/java/net/openhft/lang/model/DataValueMetaModel.java new file mode 100644 index 0000000..a381ad0 --- /dev/null +++ b/lang-sandbox/src/main/java/net/openhft/lang/model/DataValueMetaModel.java @@ -0,0 +1,60 @@ +/* + * Copyright 2014 Higher Frequency Trading + * + * http://www.higherfrequencytrading.com + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package net.openhft.lang.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; + +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); + } + } + + void addIgnoredClass(Class aClass) { + ignoredClasses.add(aClass); + } + + 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-sandbox/src/main/java/net/openhft/lang/model/MethodFilter.java b/lang-sandbox/src/main/java/net/openhft/lang/model/MethodFilter.java new file mode 100644 index 0000000..22283ba --- /dev/null +++ b/lang-sandbox/src/main/java/net/openhft/lang/model/MethodFilter.java @@ -0,0 +1,27 @@ +/* + * Copyright 2014 Higher Frequency Trading + * + * http://www.higherfrequencytrading.com + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package net.openhft.lang.model; + +import java.lang.reflect.Method; + +interface MethodFilter { + int matches(); + + String nameFor(Method method, Class<?>[] parameterTypes); +} diff --git a/lang-sandbox/src/main/java/net/openhft/lang/model/MethodTemplate.java b/lang-sandbox/src/main/java/net/openhft/lang/model/MethodTemplate.java new file mode 100644 index 0000000..f41e502 --- /dev/null +++ b/lang-sandbox/src/main/java/net/openhft/lang/model/MethodTemplate.java @@ -0,0 +1,25 @@ +/* + * Copyright 2014 Higher Frequency Trading + * + * http://www.higherfrequencytrading.com + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package net.openhft.lang.model; + +import java.lang.reflect.Method; + +public interface MethodTemplate { + void generate(Method method, ClassModel classModel); +} diff --git a/lang-sandbox/src/main/java/net/openhft/lang/model/VanillaFilter.java b/lang-sandbox/src/main/java/net/openhft/lang/model/VanillaFilter.java new file mode 100644 index 0000000..b825675 --- /dev/null +++ b/lang-sandbox/src/main/java/net/openhft/lang/model/VanillaFilter.java @@ -0,0 +1,177 @@ +/* + * Copyright 2014 Higher Frequency Trading + * + * http://www.higherfrequencytrading.com + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package net.openhft.lang.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-sandbox/src/test/java/net/openhft/lang/io/OffHeapReadWriteLockTest.java b/lang-sandbox/src/test/java/net/openhft/lang/io/OffHeapReadWriteLockTest.java new file mode 100644 index 0000000..7270a09 --- /dev/null +++ b/lang-sandbox/src/test/java/net/openhft/lang/io/OffHeapReadWriteLockTest.java @@ -0,0 +1,77 @@ +/* + * Copyright 2014 Higher Frequency Trading + * + * http://www.higherfrequencytrading.com + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package net.openhft.lang.io; + +import net.openhft.lang.io.DirectStore; +import net.openhft.lang.io.OffHeapReadWriteLock; +import org.junit.Test; + +import static org.junit.Assert.*; + +public class OffHeapReadWriteLockTest { + @Test + public void readCycle() { + OffHeapReadWriteLock ohrwl = new OffHeapReadWriteLock(DirectStore.allocate(64).bytes(), 0); + assertEquals("none w:0/0 r:0/0", ohrwl.toString()); + assertTrue(ohrwl.readLock().tryLock()); + assertEquals("read w:0/0 r:1/0", ohrwl.toString()); + assertTrue(ohrwl.readLock().tryLock()); + assertEquals("read w:0/0 r:2/0", ohrwl.toString()); + ohrwl.readLock().unlock(); + assertEquals("read w:0/0 r:1/0", ohrwl.toString()); + ohrwl.readLock().unlock(); + assertEquals("none w:0/0 r:0/0", ohrwl.toString()); + } + + @Test + public void writeCycle() { + OffHeapReadWriteLock ohrwl = new OffHeapReadWriteLock(DirectStore.allocate(64).bytes(), 0); + assertEquals("none w:0/0 r:0/0", ohrwl.toString()); + assertTrue(ohrwl.writeLock().tryLock()); + assertEquals("write w:1/0 r:0/0", ohrwl.toString()); + assertFalse(ohrwl.writeLock().tryLock()); + ohrwl.writeLock().addWaitingWriter(1); + assertEquals("write w:1/1 r:0/0", ohrwl.toString()); + ohrwl.writeLock().unlock(); + assertEquals("write w:0/1 r:0/0", ohrwl.toString()); + assertTrue(ohrwl.writeLock().tryLock()); + ohrwl.writeLock().addWaitingWriter(-1); + assertEquals("write w:1/0 r:0/0", ohrwl.toString()); + ohrwl.writeLock().unlock(); + assertEquals("none w:0/0 r:0/0", ohrwl.toString()); + } + + @Test + public void waitingCycle() { + OffHeapReadWriteLock ohrwl = new OffHeapReadWriteLock(DirectStore.allocate(64).bytes(), 0); + assertEquals("none w:0/0 r:0/0", ohrwl.toString()); + assertTrue(ohrwl.writeLock().tryLock()); + assertEquals("write w:1/0 r:0/0", ohrwl.toString()); + assertFalse(ohrwl.readLock().tryLock()); + ohrwl.readLock().addWaitingReader(2); + assertEquals("write w:1/0 r:0/2", ohrwl.toString()); + ohrwl.writeLock().unlock(); + assertEquals("none w:0/0 r:0/2", ohrwl.toString()); + assertTrue(ohrwl.readLock().tryLock()); + ohrwl.readLock().addWaitingReader(-1); + assertEquals("read w:0/0 r:1/1", ohrwl.toString()); + assertTrue(ohrwl.readLock().tryLock()); + ohrwl.readLock().addWaitingReader(-1); + } +}
\ No newline at end of file diff --git a/lang-sandbox/src/test/java/net/openhft/lang/io/serialization/RawCopierTest.java b/lang-sandbox/src/test/java/net/openhft/lang/io/serialization/RawCopierTest.java new file mode 100644 index 0000000..53d7abf --- /dev/null +++ b/lang-sandbox/src/test/java/net/openhft/lang/io/serialization/RawCopierTest.java @@ -0,0 +1,95 @@ +/* + * Copyright 2014 Higher Frequency Trading + * + * http://www.higherfrequencytrading.com + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package net.openhft.lang.io.serialization; + +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 = DirectStore.allocate(1024); + DirectBytes db = ds.bytes(); + 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-sandbox/src/test/java/net/openhft/lang/io/serialization/direct/DirectSerializationFilterTest.java b/lang-sandbox/src/test/java/net/openhft/lang/io/serialization/direct/DirectSerializationFilterTest.java new file mode 100644 index 0000000..16f6b8b --- /dev/null +++ b/lang-sandbox/src/test/java/net/openhft/lang/io/serialization/direct/DirectSerializationFilterTest.java @@ -0,0 +1,114 @@ +/* + * Copyright 2014 Higher Frequency Trading + * + * http://www.higherfrequencytrading.com + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package net.openhft.lang.io.serialization.direct; + +import org.junit.Test; + +import java.lang.reflect.Field; +import java.util.ArrayList; +import java.util.List; + +import static net.openhft.lang.io.serialization.direct.DirectSerializationFilter.stopAtFirstIneligibleField; +import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertTrue; + +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-sandbox/src/test/java/net/openhft/lang/io/serialization/direct/DirectSerializationMetadataTest.java b/lang-sandbox/src/test/java/net/openhft/lang/io/serialization/direct/DirectSerializationMetadataTest.java new file mode 100644 index 0000000..4642107 --- /dev/null +++ b/lang-sandbox/src/test/java/net/openhft/lang/io/serialization/direct/DirectSerializationMetadataTest.java @@ -0,0 +1,96 @@ +/* + * Copyright 2014 Higher Frequency Trading + * + * http://www.higherfrequencytrading.com + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package net.openhft.lang.io.serialization.direct; + +import org.junit.Test; + +import 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-sandbox/src/test/java/net/openhft/lang/io/serialization/direct/IntrospectTest.java b/lang-sandbox/src/test/java/net/openhft/lang/io/serialization/direct/IntrospectTest.java new file mode 100644 index 0000000..609d020 --- /dev/null +++ b/lang-sandbox/src/test/java/net/openhft/lang/io/serialization/direct/IntrospectTest.java @@ -0,0 +1,57 @@ +/* + * Copyright 2014 Higher Frequency Trading + * + * http://www.higherfrequencytrading.com + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package net.openhft.lang.io.serialization.direct; + +import org.junit.Test; + +import java.lang.reflect.Field; +import java.util.ArrayList; +import java.util.List; + +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-sandbox/src/test/java/net/openhft/lang/io/serialization/direct/ObjectMarshallerTest.java b/lang-sandbox/src/test/java/net/openhft/lang/io/serialization/direct/ObjectMarshallerTest.java new file mode 100644 index 0000000..7c7efc9 --- /dev/null +++ b/lang-sandbox/src/test/java/net/openhft/lang/io/serialization/direct/ObjectMarshallerTest.java @@ -0,0 +1,263 @@ +/* + * Copyright 2014 Higher Frequency Trading + * + * http://www.higherfrequencytrading.com + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package net.openhft.lang.io.serialization.direct; + +import net.openhft.lang.io.ByteBufferBytes; +import net.openhft.lang.io.Bytes; +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.*; + +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.clear(); + marshaller.read(b, p); + + assertEquals(55, p.a); + + b.clear(); + + 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.clear(); + marshaller.read(b, p); + + assertEquals(55, p.a); + assertEquals(-10, p.b); + + b.clear(); + + 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.clear(); + marshaller.read(b, p); + + assertEquals(55, p.a); + assertEquals(-10, p.b); + assertEquals(92, p.c); + + b.clear(); + + 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.clear(); + marshaller.read(b, p); + + assertTrue(p.a); + + b.clear(); + + 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.clear(); + marshaller.read(b, p); + + assertTrue(p.a); + assertEquals(Long.MIN_VALUE, p.b); + + b.clear(); + + 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.clear(); + 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.clear(); + + 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.clear(); + 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.clear(); + + 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 Bytes createByteStore() { + return ByteBufferBytes.wrap(ByteBuffer.allocate(64)); + } +}
\ No newline at end of file diff --git a/lang-sandbox/src/test/java/net/openhft/lang/io/serialization/direct/TestClasses.java b/lang-sandbox/src/test/java/net/openhft/lang/io/serialization/direct/TestClasses.java new file mode 100644 index 0000000..420c2f2 --- /dev/null +++ b/lang-sandbox/src/test/java/net/openhft/lang/io/serialization/direct/TestClasses.java @@ -0,0 +1,109 @@ +/* + * Copyright 2014 Higher Frequency Trading + * + * http://www.higherfrequencytrading.com + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package net.openhft.lang.io.serialization.direct; + +import java.util.Collections; +import java.util.List; + +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; + } +} |