[
  {
    "path": ".gitignore",
    "content": "target\n"
  },
  {
    "path": "LICENSE",
    "content": "Copyright (C) 2013 Pekka Enberg\n\nFalcon is distributed under the 2-clause BSD license:\n\n    Redistribution and use in source and binary forms, with or without\n    modification, are permitted provided that the following conditions are met:\n\n    * Redistributions of source code must retain the above copyright notice,\n      this list of conditions and the following disclaimer.\n\n    * Redistributions in binary form must reproduce the above copyright notice,\n      this list of conditions and the following disclaimer in the documentation\n      and/or other materials provided with the distribution.\n\n    THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS \"AS IS\"\n    AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE\n    IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE\n    DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE\n    FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL\n    DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR\n    SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER\n    CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,\n    OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE\n    OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.\n"
  },
  {
    "path": "README.md",
    "content": "# Falcon\n\nFalcon is a high performance, low latency FIX engine for the JVM. It provides\nan API that enables FIX connectivity for both buy side and sell side\napplications such as trading systems and order management systems.\n\nThe engine is designed to avoid heap allocations on the TX and RX paths to\navoid GC pauses that are disastrous for low-latency applications. The engine\nis packed with other optimizations such as avoiding querying the system clock\nfor every message and open-coding formatting and parsing functions where the\nJRE allocates memory implicitly.\n\nFalcon is able to achieve 8 µs RTT on when running the latency tester client\nand server on the same machine.\n\n## Features\n\n* Zero-copy, non-blocking, low-latency NIO networking\n* Low heap allocation rate in the FIX engine core\n* Small memory footprint for session and message data structures\n\n## Example\n\nAn example application that sends 100000 ``NewOrderSingle`` messages looks like\nthis:\n\n```java\nimport static java.net.StandardSocketOptions.*;\nimport java.nio.channels.*;\nimport java.net.*;\nimport java.nio.*;\n\nimport static falcon.fix.MessageTypes.*;\nimport static falcon.fix.Versions.*;\nimport static falcon.fix.Tags.*;\nimport falcon.fix.*;\n\npublic class Example {\n  public static void main(String[] args) throws Exception {\n    SocketChannel socket = connect(\"localhost\", 7070);\n\n    Session session = new Session(socket, FIX_4_2, \"HERMES\", \"INET\");\n\n    session.updateTime();\n\n    session.send(new Message.Builder(Logon)\n        .add(new Field(EncryptMethod, \"0\" ))\n        .add(new Field(HeartBtInt,    \"30\"))\n        .build());\n\n    Message newOrder =\n      new Message.Builder(NewOrderSingle)\n          .add(new Field(EncryptMethod, \"0\" ))\n          .add(new Field(HeartBtInt,    \"30\"))\n          .build();\n\n    for (int i = 0; i < 100000; i++) {\n      if ((i % 10000) == 0) {\n        session.updateTime();\n      }\n      session.send(newOrder);\n    }\n\n    session.updateTime();\n\n    session.send(new Message.Builder(Logout).build());\n\n    socket.close();\n  }\n\n  private static SocketChannel connect(String host, int port) throws Exception {\n    InetSocketAddress addr = new InetSocketAddress(host, port);\n    SocketChannel socket = SocketChannel.open();\n    socket.configureBlocking(false);\n    socket.setOption(TCP_NODELAY, true);\n    socket.connect(addr);\n    socket.finishConnect();\n    return socket;\n  }\n}\n```\n\n## Performance\n\nThe FIX engine has been measured to have 8 µs RTT for a loopback ping-pong test\nwhere client sends a ``NewOrderSingle`` message and waits for an\n``ExecutionReport`` message to arrive. The numbers include the time spent in\nLinux TCP/IP stack and the loopback device.\n\nTo reproduce the results, first download and build [Libtrading]. Then start the\nFIX performance test server:\n\n```\n$ taskset -c 0 tools/fix/fix_server -m 1 -p 7070\n```\n\nFinally, run the Falcon latency tests:\n\n```\n$ ./falcon-perf-test/bin/falcon-perf-test 1000000\n87693.5 messages/second\nmin/avg/max = 9.8/11.4/19935.3 µs\nPercentiles:\n  1.00%: 10.15 µs\n  5.00%: 10.51 µs\n 10.00%: 10.61 µs\n 50.00%: 11.12 µs\n 90.00%: 11.90 µs\n 95.00%: 13.27 µs\n 99.00%: 14.53 µs\n```\n\n  [Libtrading]: https://github.com/libtrading/libtrading\n\n## License\n\nCopyright © 2013-2015 Pekka Enberg and contributors\n\nFalcon is distributed under the 2-clause BSD license.\n"
  },
  {
    "path": "falcon/pom.xml",
    "content": "<?xml version=\"1.0\"?>\n<project xmlns=\"http://maven.apache.org/POM/4.0.0\" xmlns:xsi=\"http://www.w3.org/2001/XMLSchema-instance\"\n  xsi:schemaLocation=\"http://maven.apache.org/POM/4.0.0 http://maven.apache.org/maven-v4_0_0.xsd\">\n  <modelVersion>4.0.0</modelVersion>\n  <parent>\n    <groupId>falcon</groupId>\n    <artifactId>falcon-parent</artifactId>\n    <version>0.1.0-master-SNAPSHOT</version>\n  </parent>\n\n  <groupId>falcon</groupId>\n  <artifactId>falcon</artifactId>\n  <name>Falcon</name>\n\n  <dependencies>\n    <dependency>\n      <groupId>com.github.stephenc</groupId>\n      <artifactId>jamm</artifactId>\n      <scope>test</scope>\n    </dependency>\n    <dependency>\n      <groupId>junit</groupId>\n      <artifactId>junit</artifactId>\n      <scope>test</scope>\n    </dependency>\n  </dependencies>\n\n  <build>\n    <plugins>\n      <plugin>\n        <groupId>org.apache.maven.plugins</groupId>\n        <artifactId>maven-dependency-plugin</artifactId>\n        <executions>\n          <execution>\n            <id>copy-dependencies</id>\n            <phase>generate-test-resources</phase>\n            <goals>\n              <goal>copy</goal>\n            </goals>\n            <configuration>\n              <artifactItems>\n                <artifactItem>\n                  <groupId>com.github.stephenc</groupId>\n                  <artifactId>jamm</artifactId>\n                  <type>jar</type>\n                  <outputDirectory>${project.build.directory}</outputDirectory>\n                  <destFileName>jamm.jar</destFileName>\n                </artifactItem>\n              </artifactItems>\n            </configuration>\n          </execution>\n        </executions>\n      </plugin>\n      <plugin>\n        <groupId>org.apache.maven.plugins</groupId>\n        <artifactId>maven-surefire-plugin</artifactId>\n        <configuration>\n          <argLine>-javaagent:${project.build.directory}/jamm.jar</argLine>\n        </configuration>\n      </plugin>\n    </plugins>\n  </build>\n</project>\n"
  },
  {
    "path": "falcon/src/main/java/falcon/fix/ByteString.java",
    "content": "package falcon.fix;\n\nimport java.nio.ByteBuffer;\n\npublic final class ByteString {\n  final byte[] data;\n\n  /**\n   * Returns a new byte string that contains a copy of <code>len</code> bytes from <code>buf</code>.\n   */\n  public static ByteString of(ByteBuffer buf, int len) {\n    byte[] data = new byte[len];\n    buf.get(data);\n    return new ByteString(data);\n  }\n\n  public ByteString(byte[] data) {\n    this.data = data;\n  }\n}\n"
  },
  {
    "path": "falcon/src/main/java/falcon/fix/Field.java",
    "content": "package falcon.fix;\n\nimport java.nio.ByteBuffer;\n\n/**\n * A field is composed of a tag and value pair.\n */\npublic class Field {\n\n  private int tag;\n  private Object value;\n\n  public Field(int tag, Object value) {\n    this.tag   = tag;\n    this.value = value;\n  }\n\n  public int tag() {\n    return tag;\n  }\n\n  public Object value() {\n    return value;\n  }\n}\n"
  },
  {
    "path": "falcon/src/main/java/falcon/fix/Message.java",
    "content": "package falcon.fix;\n\nimport static falcon.fix.Tags.*;\nimport java.nio.ByteBuffer;\nimport java.util.ArrayList;\nimport java.util.List;\n\n/**\n * A message is composed of number of fields, grouped in a header, body, and a\n * trailer.\n */\npublic class Message {\n\n  private List<Field>  fields;\n  private MessageType  type;\n\n  /**\n   * Constructs a message of specified type and fields.\n   *\n   * @param type   Type of this message.\n   * @param fields Fields for this message.\n   *\n   * The constructed message has a header and a trailer, and the specified fields.\n   */\n  public Message(MessageType type, List<Field> fields) {\n    this.type   = type;\n    this.fields = fields;\n  }\n\n  public MessageType type() {\n    return type;\n  }\n\n  public List<Field> fields() {\n    return fields;\n  }\n\n  /**\n   * Message builder.\n   */\n  public static class Builder {\n    private List<Field>  fields = new ArrayList<>();\n    private MessageType type;\n\n    public Builder(MessageType type) {\n      this.type = type;\n    }\n\n    public Builder add(Field field) {\n      fields.add(field);\n      return this;\n    }\n\n    public Message build() {\n      return new Message(type, fields);\n    }\n  }\n}\n"
  },
  {
    "path": "falcon/src/main/java/falcon/fix/MessageType.java",
    "content": "package falcon.fix;\n\n/**\n * A message type specifies which fields a message contains.\n */\npublic class MessageType {\n\n  private String value;\n\n  public MessageType(String value) {\n    this.value = value;\n  }\n\n  public String value() {\n    return value;\n  }\n}\n"
  },
  {
    "path": "falcon/src/main/java/falcon/fix/MessageTypes.java",
    "content": "package falcon.fix;\n\npublic class MessageTypes {\n\n  public static final MessageType Heartbeat      = new MessageType(\"0\");\n  public static final MessageType TestRequest    = new MessageType(\"1\");\n  public static final MessageType ResendRequest  = new MessageType(\"2\");\n  public static final MessageType Reject         = new MessageType(\"3\");\n  public static final MessageType SequenceReset  = new MessageType(\"4\");\n  public static final MessageType Logout         = new MessageType(\"5\");\n  public static final MessageType ExecutionReport= new MessageType(\"8\");\n  public static final MessageType Logon          = new MessageType(\"A\");\n  public static final MessageType NewOrderSingle = new MessageType(\"D\");\n\n}\n"
  },
  {
    "path": "falcon/src/main/java/falcon/fix/ParseException.java",
    "content": "package falcon.fix;\n\npublic class ParseException extends Exception {\n  public ParseException() {\n  }\n\n  public ParseException(String message) {\n    super(message);\n  }\n}\n"
  },
  {
    "path": "falcon/src/main/java/falcon/fix/ParseFailedException.java",
    "content": "package falcon.fix;\n\npublic class ParseFailedException extends ParseException {\n  public ParseFailedException(String message) {\n    super(message);\n  }\n}\n"
  },
  {
    "path": "falcon/src/main/java/falcon/fix/PartialParseException.java",
    "content": "package falcon.fix;\n\npublic class PartialParseException extends ParseException {\n}\n"
  },
  {
    "path": "falcon/src/main/java/falcon/fix/Protocol.java",
    "content": "package falcon.fix;\n\nimport static falcon.fix.MessageTypes.*;\n\nimport java.nio.ByteBuffer;\n\n/**\n * On-wire protocol parsing and formatting.\n */\npublic class Protocol {\n\n  /*\n   * Maximum message header size in bytes.\n   */\n  public static final int MAX_HEADER_SIZE = 64;\n\n  /*\n   * Maximum message body size in bytes.\n   */\n  public static final int MAX_BODY_SIZE = 4096;\n\n  /**\n   * Format field to on-wire format.\n   */\n  public static void format(ByteBuffer buf, int tag, byte[] value) {\n    writeInt(buf, tag);\n    buf.put((byte) '=');\n    buf.put(value);\n    buf.put((byte) 0x01);\n  }\n\n  /**\n   * Format field to on-wire format.\n   */\n  public static void formatInt(ByteBuffer buf, int tag, int value) {\n    writeInt(buf, tag);\n    buf.put((byte) '=');\n    writeInt(buf, value);\n    buf.put((byte) 0x01);\n  }\n\n  public static void formatString(ByteBuffer buf, int tag, String value) {\n    writeInt(buf, tag);\n    buf.put((byte) '=');\n    for (int i = 0; i < value.length(); i++) {\n      buf.put((byte) value.charAt(i));\n    }\n    buf.put((byte) 0x01);\n  }\n\n  public static void formatCheckSum(ByteBuffer buf, int tag, int value) {\n    writeInt(buf, tag);\n    buf.put((byte) '=');\n    if (value < 10) {\n      buf.put((byte) '0');\n    }\n    if (value < 100) {\n      buf.put((byte) '0');\n    }\n    writeInt(buf, value);\n    buf.put((byte) 0x01);\n  }\n\n  public static void writeInt(ByteBuffer buf, int n) {\n    if (n < 0) {\n      buf.put((byte) '-');\n    }\n    n = Math.abs(n);\n    int start = buf.position();\n    do {\n      buf.put((byte)('0' + n % 10));\n      n /= 10;\n    } while (n > 0);\n    int end = buf.position();\n    int i = start;\n    int j = end - 1;\n    while (i < j) {\n      byte tmp = buf.get(i);\n      buf.put(i, buf.get(j));\n      buf.put(j, tmp);\n      i++; j--;\n    }\n  }\n\n  public static void match(ByteBuffer buf, int tag) throws ParseException {\n    matchTag(buf, tag);\n    while (buf.get() != (byte)0x01)\n      ;;\n  }\n\n  public static int matchInt(ByteBuffer buf, int tag) throws ParseException {\n    matchTag(buf, tag);\n    return parseInt(buf, (byte)0x01);\n  }\n\n  public static int parseInt(ByteBuffer buf, byte delimiter) {\n    int sign = 1;\n    if (buf.get(buf.position()) == (byte)'-') {\n      buf.get();\n      sign = -1;\n    }\n    int result = 0;\n    for (;;) {\n      byte ch = buf.get();\n      if (ch == delimiter) {\n        break;\n      }\n      result *= 10;\n      result += (byte)ch - (byte)'0';\n    }\n    return sign * result;\n  }\n\n  public static ByteString parseString(ByteBuffer buf, byte delimiter) {\n    int start = buf.position();\n    for (;;) {\n      byte ch = buf.get();\n      if (ch == delimiter) {\n        break;\n      }\n    }\n    int end = buf.position() - 1;\n    buf.position(start);\n    return ByteString.of(buf, end-start);\n  }\n\n  public static MessageType matchMsgType(ByteBuffer buf) throws ParseException {\n    matchTag(buf, Tags.MsgType);\n    MessageType result = null;\n    switch (buf.get()) {\n    case (byte)'0': result = Heartbeat;       break;\n    case (byte)'1': result = TestRequest;     break;\n    case (byte)'2': result = ResendRequest;   break;\n    case (byte)'3': result = Reject;          break;\n    case (byte)'4': result = SequenceReset;   break;\n    case (byte)'5': result = Logout;          break;\n    case (byte)'8': result = ExecutionReport; break;\n    case (byte)'A': result = Logon;           break;\n    case (byte)'D': result = NewOrderSingle;  break;\n    case (byte)0x01:\n      throw new ParseFailedException(\"Invalid MsgType (35)\");\n    default:\n      throw new ParseFailedException(\"Tag specified without a value\");\n    }\n    if (buf.get() != (byte)0x01) {\n      throw new ParseFailedException(\"Invalid MsgType (35)\");\n    }\n    return result;\n  }\n\n  public static void matchTag(ByteBuffer buf, int tag) throws ParseException {\n    int actual = parseInt(buf, (byte)'=');\n    if (actual != tag) {\n      throw new ParseFailedException(\"Required tag missing\");\n    }\n  }\n}\n"
  },
  {
    "path": "falcon/src/main/java/falcon/fix/Session.java",
    "content": "package falcon.fix;\n\nimport static falcon.fix.Tags.*;\nimport java.nio.channels.*;\nimport java.text.*;\nimport java.util.*;\nimport java.nio.*;\n\n/**\n * A session is bi-directional stream of messages between two parties where\n * message ordering is guaranteed with monotonically increasing message\n * sequence numbers.\n */\npublic class Session {\n\n  private ByteBuffer headBuf = ByteBuffer.allocate(Protocol.MAX_HEADER_SIZE);\n  private ByteBuffer bodyBuf = ByteBuffer.allocate(Protocol.MAX_BODY_SIZE);\n  private ByteBuffer rxBuf = ByteBuffer.allocate(Protocol.MAX_HEADER_SIZE + Protocol.MAX_BODY_SIZE);\n\n  private SimpleDateFormat dateFormat = new SimpleDateFormat(\"yyyyMMdd-HH:mm:ss.SSS\");\n  private SocketChannel socket;\n  private String senderCompId;\n  private String targetCompId;\n  private Version version;\n  private int sequence;\n  private String now;\n\n  public Session(SocketChannel socket, Version version, String senderCompId, String targetCompId) {\n    this.socket       = socket;\n    this.version      = version;\n    this.senderCompId = senderCompId;\n    this.targetCompId = targetCompId;\n  }\n\n  public void updateTime() {\n    now = dateFormat.format(new Date());\n  }\n\n  public void send(Message msg) throws Exception {\n    Protocol.formatString(bodyBuf, MsgType, msg.type().value());\n    Protocol.formatString(bodyBuf, SenderCompID, senderCompId);\n    Protocol.formatString(bodyBuf, TargetCompID, targetCompId);\n    Protocol.formatInt(bodyBuf, MsgSeqNum, sequence++);\n    Protocol.formatString(bodyBuf, SendingTime, now);\n\n    List<Field> fields = msg.fields();\n    for (int i = 0; i < fields.size(); i++) {\n      Field field = fields.get(i);\n      Object value = field.value();\n      if (value instanceof String) {\n        Protocol.formatString(bodyBuf, field.tag(), (String) field.value());\n      } else if (value instanceof Integer) {\n        Protocol.formatInt(bodyBuf, field.tag(), ((Integer) field.value()).intValue());\n      } else {\n        throw new IllegalStateException();\n      }\n    }\n\n    Protocol.format(headBuf, BeginString, version.value());\n    Protocol.formatInt(headBuf, BodyLength, bodyBuf.position());\n\n    Protocol.formatCheckSum(bodyBuf, CheckSum, (sum(headBuf) + sum(bodyBuf)) % 256);\n\n    headBuf.flip();\n    bodyBuf.flip();\n\n    while (headBuf.remaining() > 0) {\n      socket.write(headBuf);\n    }\n    while (bodyBuf.remaining() > 0) {\n      socket.write(bodyBuf);\n    }\n\n    headBuf.clear();\n    bodyBuf.clear();\n  }\n\n  public Message recv() throws Exception {\n    int count = socket.read(rxBuf);\n    if (count <= 0) {\n      return null;\n    }\n    rxBuf.flip();\n    rxBuf.mark();\n    List<Field> fields = new ArrayList<Field>();\n    MessageType msgType = null;\n    try {\n      Protocol.match(rxBuf, BeginString);\n      int bodyLen = Protocol.matchInt(rxBuf, BodyLength);\n      int msgTypeOffset = rxBuf.position();\n      msgType = Protocol.matchMsgType(rxBuf);\n      int checksumOffset = msgTypeOffset + bodyLen;\n      rxBuf.position(checksumOffset);\n      int checksumActual = sum(rxBuf) % 256;\n      int checksumExpected = Protocol.matchInt(rxBuf, CheckSum);\n      if (checksumExpected != checksumActual) {\n        throw new RuntimeException(String.format(\"Invalid checksum: expected %d, got: %d\", checksumExpected, checksumActual));\n      }\n      int endOffset = rxBuf.position();\n      rxBuf.position(msgTypeOffset);\n      while (rxBuf.position() < checksumOffset) {\n        int tag = Protocol.parseInt(rxBuf, (byte)'=');\n        ByteString value = Protocol.parseString(rxBuf, (byte)0x01);\n        fields.add(new Field(tag, value));\n      }\n      rxBuf.position(endOffset);\n    } catch (PartialParseException e) {\n      rxBuf.reset();\n      return null;\n    } catch (ParseFailedException e) {\n      throw new RuntimeException(\"Garbled message\", e);\n    }\n    rxBuf.compact();\n    return new Message(msgType, fields);\n  }\n\n  private static int sum(ByteBuffer buf) {\n    return sum(buf, 0, buf.position());\n  }\n\n  private static int sum(ByteBuffer buf, int start, int end) {\n    int result = 0;\n    for (int i = start; i < end; i++)\n      result += buf.get(i);\n    return result;\n  }\n}\n"
  },
  {
    "path": "falcon/src/main/java/falcon/fix/Tags.java",
    "content": "package falcon.fix;\n\npublic class Tags {\n\n  public static final int BeginString   = 8;\n  public static final int BodyLength    = 9;\n  public static final int CheckSum      = 10;\n  public static final int MsgSeqNum     = 34;\n  public static final int MsgType       = 35;\n  public static final int SenderCompID  = 49;\n  public static final int SendingTime   = 52;\n  public static final int TargetCompID  = 56;\n  public static final int EncryptMethod = 98;\n  public static final int HeartBtInt    = 108;\n}\n"
  },
  {
    "path": "falcon/src/main/java/falcon/fix/Version.java",
    "content": "package falcon.fix;\n\n/**\n * A version specifies which base standard is followed in a session.\n *\n * Session version is specified in the <b>BeginString</b> tag in message\n * header. Although not supported by the standard, many dialects are a\n * combination of two or more versions. <b>BeginString</b> thus only represents\n * the base specification and message types and tags are typically specified in\n * proprietary trading venue specifications. Session level semantics usually\n * follow the specified base standard, but not always.\n */\npublic class Version {\n\n  private byte[] value;\n\n  public Version(String value) {\n    this(value.getBytes());\n  }\n\n  public Version(byte[] value) {\n    this.value = value;\n  }\n\n  public byte[] value() {\n    return value;\n  }\n}\n"
  },
  {
    "path": "falcon/src/main/java/falcon/fix/Versions.java",
    "content": "package falcon.fix;\n\npublic class Versions {\n\n  /**\n   * FIX 4.1 based sessions.\n   */\n  public static Version FIX_4_1 = new Version(\"FIX.4.1\");\n\n  /**\n   * FIX 4.2 based sessions.\n   */\n  public static Version FIX_4_2 = new Version(\"FIX.4.2\");\n\n  /**\n   * FIX 4.3 based sessions.\n   */\n  public static Version FIX_4_3 = new Version(\"FIX.4.3\");\n\n  /**\n   * FIX 4.4 based sessions.\n   */\n  public static Version FIX_4_4 = new Version(\"FIX.4.4\");\n}\n"
  },
  {
    "path": "falcon/src/main/java/falcon/fix/package-info.java",
    "content": "/**\n * Falcon FIX core classes.\n */\npackage falcon.fix;\n"
  },
  {
    "path": "falcon/src/test/java/falcon/fix/MemoryMeterTest.java",
    "content": "package falcon.fix;\n\nimport static falcon.fix.MessageTypes.*;\nimport static falcon.fix.Versions.*;\nimport static falcon.fix.Tags.*;\n\nimport org.github.jamm.MemoryMeter;\nimport java.nio.channels.*;\nimport org.junit.Test;\n\npublic class MemoryMeterTest {\n\n  private MemoryMeter meter = new MemoryMeter();\n\n  @Test\n  public void footprint() throws Exception {\n    Session session = new Session(SocketChannel.open(), FIX_4_2, \"HERMES\", \"INET\");\n    measure(session);\n\n    Message msg =\n      new Message.Builder(Logon)\n        .add(new Field(EncryptMethod, \"0\" ))\n        .add(new Field(HeartBtInt,    \"30\"))\n        .build();\n    measure(msg);\n\n    Field field = new Field(EncryptMethod, \"0\" );\n    measure(field);\n  }\n\n  private void measure(Object obj) {\n    System.out.printf(\"Memory footprint of '%s':\\n\\n\", obj.getClass().getName());\n    System.out.printf(\"  Size (shallow): %d bytes\\n\", meter.measure(obj));\n    System.out.printf(\"  Size (deep)   : %d bytes\\n\", meter.measureDeep(obj));\n    System.out.printf(\"  Child objects : %d\\n\", meter.countChildren(obj));\n    System.out.printf(\"\\n\");\n  }\n}\n"
  },
  {
    "path": "falcon/src/test/java/falcon/fix/ProtocolTest.java",
    "content": "package falcon.fix;\n\nimport static org.junit.Assert.*;\nimport java.nio.charset.*;\nimport org.junit.Test;\nimport java.nio.*;\n\npublic class ProtocolTest {\n  @Test\n  public void writeInt() throws Exception {\n    assertEquals(\"-123\", writeInt(-123));\n    assertEquals(   \"0\", writeInt(0));\n    assertEquals( \"123\", writeInt(123));\n  }\n\n  private static String writeInt(int n) throws Exception {\n    ByteBuffer buf = ByteBuffer.allocate(128);\n    Protocol.writeInt(buf, n);\n    buf.flip();\n    byte[] bytes = new byte[buf.remaining()];\n    buf.get(bytes);\n    return new String(bytes, \"ASCII\");\n  }\n\n  @Test\n  public void parseInt() throws Exception {\n    assertEquals(-123, parseInt(\"-123\\1\"));\n    assertEquals(   0, parseInt(\"0\\1\"));\n    assertEquals( 123, parseInt(\"123\\1\"));\n  }\n\n  private static int parseInt(String s) throws Exception {\n    Charset charset = Charset.forName(\"UTF-8\");\n    CharsetEncoder encoder = charset.newEncoder();\n    ByteBuffer buf = encoder.encode(CharBuffer.wrap(s));\n    return Protocol.parseInt(buf, (byte)0x01);\n  }\n}\n"
  },
  {
    "path": "falcon-perf-test/bin/falcon-perf-test",
    "content": "#!/bin/sh\n\nif [ -z \"$JAVACMD\" ] ; then\n  if [ -z \"$JAVA_HOME\" ] ; then\n    JAVACMD='java'\n  else\n    JAVACMD=\"$JAVA_HOME/bin/java\"\n  fi\nfi\n\njava_class=falcon.fix.perf.ClientPerfTest\n\nJAVA_OPTS=\"-verbose:gc -cp falcon-perf-test/target/falcon-perf-test-0.1.0-master-SNAPSHOT-jar-with-dependencies.jar\"\n\n$JAVACMD $JAVA_OPTS $java_class $@\n"
  },
  {
    "path": "falcon-perf-test/pom.xml",
    "content": "<?xml version=\"1.0\"?>\n<project xmlns=\"http://maven.apache.org/POM/4.0.0\" xmlns:xsi=\"http://www.w3.org/2001/XMLSchema-instance\"\n  xsi:schemaLocation=\"http://maven.apache.org/POM/4.0.0 http://maven.apache.org/maven-v4_0_0.xsd\">\n  <modelVersion>4.0.0</modelVersion>\n  <parent>\n    <groupId>falcon</groupId>\n    <artifactId>falcon-parent</artifactId>\n    <version>0.1.0-master-SNAPSHOT</version>\n  </parent>\n\n  <groupId>falcon</groupId>\n  <artifactId>falcon-perf-test</artifactId>\n  <name>Falcon Performance Test</name>\n\n  <dependencies>\n    <dependency>\n      <groupId>falcon</groupId>\n      <artifactId>falcon</artifactId>\n      <version>${project.version}</version>\n    </dependency>\n    <dependency>\n      <groupId>org.hdrhistogram</groupId>\n      <artifactId>HdrHistogram</artifactId>\n      <version>2.1.4</version>\n    </dependency>\n  </dependencies>\n\n  <build>\n    <plugins>\n      <plugin>\n        <groupId>org.apache.maven.plugins</groupId>\n        <artifactId>maven-jar-plugin</artifactId>\n        <configuration>\n          <archive>\n            <manifest>\n              <mainClass>falcon.fix.perf.ClientPerfTest</mainClass>\n            </manifest>\n          </archive>\n        </configuration>\n      </plugin>\n      <plugin>\n        <groupId>org.apache.maven.plugins</groupId>\n        <artifactId>maven-assembly-plugin</artifactId>\n        <executions>\n          <execution>\n            <id>falcon-jar</id>\n            <phase>package</phase>\n            <goals>\n              <goal>single</goal>\n            </goals>\n            <configuration>\n              <descriptorRefs>\n                <descriptorRef>jar-with-dependencies</descriptorRef>\n              </descriptorRefs>\n              <archive>\n                <manifest>\n                  <mainClass>falcon.fix.perf.ClientPerfTest</mainClass>\n                </manifest>\n              </archive>\n            </configuration>\n          </execution>\n        </executions>\n      </plugin>\n    </plugins>\n  </build>\n</project>\n"
  },
  {
    "path": "falcon-perf-test/src/main/java/falcon/fix/perf/ClientPerfTest.java",
    "content": "package falcon.fix.perf;\n\nimport static java.net.StandardSocketOptions.TCP_NODELAY;\nimport static falcon.fix.MessageTypes.*;\nimport static falcon.fix.Versions.*;\nimport org.HdrHistogram.Histogram;\nimport static falcon.fix.Tags.*;\nimport java.nio.channels.*;\nimport java.net.*;\nimport java.nio.*;\n\nimport falcon.fix.*;\n\npublic class ClientPerfTest {\n  public static void main(String[] args) throws Exception {\n    if (args.length != 1)  {\n        System.out.printf(\"  usage: %s <iterations>\\n\", ClientPerfTest.class.getSimpleName());\n        System.exit(1);\n    }\n\n    int iterations = Integer.parseInt(args[0]);\n\n    SocketChannel socket = connect(\"localhost\", 7070);\n\n    Session session = new Session(socket, FIX_4_2, \"HERMES\", \"INET\");\n\n    session.updateTime();\n\n    session.send(new Message.Builder(Logon)\n        .add(new Field(EncryptMethod, \"0\" ))\n        .add(new Field(HeartBtInt,    \"30\"))\n        .build());\n\n    Message newOrder =\n      new Message.Builder(NewOrderSingle)\n          .add(new Field(EncryptMethod, \"0\" ))\n          .add(new Field(HeartBtInt,    \"30\"))\n          .build();\n\n    long duration = 0;\n    long min = Long.MAX_VALUE;\n    long max = 0;\n\n    Histogram histogram = new Histogram(3);\n\n    for (int i = 0; i < iterations; i++) {\n      long iterationStart = System.nanoTime();\n\n      if ((i % 10000) == 0) {\n        session.updateTime();\n      }\n\n      session.send(newOrder);\n\n      for (;;) {\n        Message msg = session.recv();\n        if (msg != null) {\n          break;\n        }\n      }\n\n      long iterationEnd = System.nanoTime();\n\n      long iterationTime = iterationEnd - iterationStart;\n\n      duration += iterationTime;\n\n      histogram.recordValue(iterationTime);\n      min = Math.min(min, iterationTime);\n      max = Math.max(max, iterationTime);\n    }\n\n    long end = System.nanoTime();\n\n    session.updateTime();\n\n    session.send(new Message.Builder(Logout).build());\n\n    socket.close();\n\n    double avg = (double)duration / (double)iterations;\n    double seconds = (double)duration / 1000000000.0;\n    System.out.printf(\"%f seconds\\n\", seconds);\n    System.out.printf(\"%.1f messages/second\\n\", (double)iterations/seconds);\n    System.out.printf(\"min/avg/max = %.1f/%.1f/%.1f µs\\n\",\n      (double)min / 1000.0, (double)avg / 1000.0, (double)max / 1000.0);\n    System.out.printf(\"Percentiles:\\n\");\n    System.out.printf(\"  1.00%%: %.2f µs\\n\", nanosToMicros(histogram.getValueAtPercentile(  1.00)));\n    System.out.printf(\"  5.00%%: %.2f µs\\n\", nanosToMicros(histogram.getValueAtPercentile(  5.00)));\n    System.out.printf(\" 10.00%%: %.2f µs\\n\", nanosToMicros(histogram.getValueAtPercentile( 10.00)));\n    System.out.printf(\" 50.00%%: %.2f µs\\n\", nanosToMicros(histogram.getValueAtPercentile( 50.00)));\n    System.out.printf(\" 90.00%%: %.2f µs\\n\", nanosToMicros(histogram.getValueAtPercentile( 90.00)));\n    System.out.printf(\" 95.00%%: %.2f µs\\n\", nanosToMicros(histogram.getValueAtPercentile( 95.00)));\n    System.out.printf(\" 99.00%%: %.2f µs\\n\", nanosToMicros(histogram.getValueAtPercentile( 99.00)));\n  }\n\n  private static SocketChannel connect(String host, int port) throws Exception {\n    InetSocketAddress addr = new InetSocketAddress(host, port);\n    SocketChannel socket = SocketChannel.open();\n    socket.configureBlocking(false);\n    socket.setOption(TCP_NODELAY, true);\n    socket.connect(addr);\n    while (!socket.finishConnect());\n    return socket;\n  }\n\n  private static double nanosToMicros(double nano) {\n    return nano / 1000.0;\n  }\n}\n"
  },
  {
    "path": "pom.xml",
    "content": "<?xml version=\"1.0\"?>\n<project xmlns=\"http://maven.apache.org/POM/4.0.0\" xmlns:xsi=\"http://www.w3.org/2001/XMLSchema-instance\"\n  xsi:schemaLocation=\"http://maven.apache.org/POM/4.0.0 http://maven.apache.org/maven-v4_0_0.xsd\">\n  <modelVersion>4.0.0</modelVersion>\n  <groupId>falcon</groupId>\n  <artifactId>falcon-parent</artifactId>\n  <name>Falcon (parent)</name>\n  <packaging>pom</packaging>\n  <version>0.1.0-master-SNAPSHOT</version>\n\n  <url>http://github.com/penberg/falcon/</url>\n  <description>FIX engine for the JVM</description>\n\n  <developers>\n    <developer>\n      <id>penberg</id>\n      <name>Pekka Enberg</name>\n      <email>penberg@iki.fi</email>\n    </developer>\n  </developers>\n\n  <licenses>\n    <license>\n      <name>The Apache Software License, Version 2.0</name>\n      <url>http://www.apache.org/licenses/LICENSE-2.0.txt</url>\n      <distribution>repo</distribution>\n    </license>\n  </licenses>\n\n  <properties>\n    <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>\n  </properties>\n\n  <scm>\n    <connection>scm:git:git@github.com:penberg/falcon.git</connection>\n    <developerConnection>scm:git:git@github.com:penberg/falcon.git</developerConnection>\n    <url>git@github.com:penberg/falcon.git</url>\n  </scm>\n\n  <modules>\n    <module>falcon</module>\n    <module>falcon-perf-test</module>\n  </modules>\n\n  <dependencyManagement>\n    <dependencies>\n      <dependency>\n        <groupId>com.github.stephenc</groupId>\n        <artifactId>jamm</artifactId>\n        <version>0.2.5</version>\n      </dependency>\n      <dependency>\n        <groupId>junit</groupId>\n        <artifactId>junit</artifactId>\n        <version>4.10</version>\n      </dependency>\n    </dependencies>\n  </dependencyManagement>\n\n  <build>\n    <pluginManagement>\n      <plugins>\n        <plugin>\n          <groupId>org.apache.maven.plugins</groupId>\n          <artifactId>maven-compiler-plugin</artifactId>\n          <version>3.1</version>\n        </plugin>\n        <plugin>\n          <groupId>org.apache.maven.plugins</groupId>\n          <artifactId>maven-dependency-plugin</artifactId>\n          <version>2.8</version>\n        </plugin>\n        <plugin>\n          <groupId>org.apache.maven.plugins</groupId>\n          <artifactId>maven-surefire-plugin</artifactId>\n          <version>2.16</version>\n        </plugin>\n        <plugin>\n          <groupId>org.apache.maven.plugins</groupId>\n          <artifactId>maven-jar-plugin</artifactId>\n          <version>2.4</version>\n        </plugin>\n        <plugin>\n          <groupId>org.apache-maven-plugins</groupId>\n          <artifactId>maven-assembly-plugin</artifactId>\n          <version>2.4</version>\n        </plugin>\n      </plugins>\n    </pluginManagement>\n    <plugins>\n      <plugin>\n        <groupId>org.apache.maven.plugins</groupId>\n        <artifactId>maven-compiler-plugin</artifactId>\n        <configuration>\n          <source>1.7</source>\n          <target>1.7</target>\n          <encoding>${project.build.sourceEncoding}</encoding>\n        </configuration>\n      </plugin>\n    </plugins>\n  </build>\n</project>\n"
  }
]