All Downloads are FREE. Search and download functionalities are using the official Maven repository.

com.barchart.feed.ddf.message.provider.CodecHelper Maven / Gradle / Ivy

/**
 * Copyright (C) 2011-2012 Barchart, Inc. 
 *
 * All rights reserved. Licensed under the OSI BSD License.
 *
 * http://www.opensource.org/licenses/bsd-license.php
 */
package com.barchart.feed.ddf.message.provider;

import static com.barchart.util.common.ascii.ASCII.NUL;
import static com.barchart.util.common.ascii.ASCII._0_;
import static com.barchart.util.common.ascii.ASCII._A_;
import static com.barchart.util.common.ascii.ASCII._J_;
import static com.barchart.util.common.ascii.ASCII._K_;
import static com.barchart.util.common.ascii.ASCII._T_;
import static com.barchart.util.common.ascii.ASCII._Z_;
import static com.barchart.util.common.ascii.ASCII.isDigit;
import static com.barchart.util.common.ascii.ASCII.isLetterUpper;

import java.nio.ByteBuffer;

import org.joda.time.DateTime;
import org.joda.time.DateTimeZone;
import org.joda.time.ReadableDateTime;
import org.w3c.dom.Element;

import com.barchart.feed.ddf.message.api.DDF_MarketBook;
import com.barchart.feed.ddf.symbol.provider.DDF_Symbology;
import com.barchart.feed.ddf.util.ClockDDF;
import com.barchart.feed.ddf.util.FeedClock;
import com.barchart.feed.ddf.util.HelperDDF;
import com.barchart.feed.ddf.util.HelperXML;
import com.barchart.util.common.ascii.ASCII;

/* TODO make public ? */

public class CodecHelper {

	// TODO make configurable
	static final boolean IS_FEED_TIME_STAMP_PRESENT = true;

	// ///////////////////////////////////////////////

	/** maximum size of ddf book snapshot on bid or ask side */
	final static int DDF_BOOK_LIMIT = DDF_MarketBook.ENTRY_LIMIT;

	/** default ddf message delay */
	static final int DDF_NO_DELAY = 0;

	/** default ddf book prices */
	static final long[] DDF_NO_PRICES = new long[DDF_BOOK_LIMIT];

	/** default ddf book sizes */
	static final long[] DDF_NO_SIZES = new long[DDF_BOOK_LIMIT];

	/** */
	static final DX_XS_Session[] DDF_NO_SESSIONS = new DX_XS_Session[0];

	/** */
	static final int DDF_NO_COUNT = 0;

	/** ddf encoded time stamp first byte */
	final static byte DDF_CENTURY = ASCII.DC4;

	/** ddf time stamp encoding/decoding mask */
	final static int DDF_TIME_STAMP_MASK = 0x40;

	// ///////////////////////////////////////////////

	// feed time stamp - "magic 9 bytes" or "current default"
	
	private static final FeedClock clock = ClockDDF.clock;
	
	static final long decodeFeedTimeStamp(final DateTimeZone zone,
			final ByteBuffer buffer) {
		if (buffer.position() == buffer.limit()) {
			// no suffix; return current
			return clock.millis();
		}
		//
		buffer.mark();
		final byte timeStampStart = buffer.get();
		buffer.reset();
		if (timeStampStart == DDF_CENTURY) {
			// magic 9 bytes
			final long time = decodeMillisUTC(zone, buffer);
			
			/* No longer setting DDF time on every message, revert to only using 
			 * DDF_ControlTimestamp messages for current time.  This was causing
			 * real time data to look delayed */
			
			// TODO This will still make delayed data look like realtime data
			// clock.set(time);
			return time;
		} else {
			// unknown suffix; return current
			return clock.millis();
		}
	}

	static final void encodeFeedTimeStamp(final long millisUTC,
			final DateTimeZone zone, final ByteBuffer buffer) {
		if (millisUTC == HelperDDF.DDF_EMPTY
				|| millisUTC == HelperDDF.DDF_CLEAR) {
			return;
		}
		if (IS_FEED_TIME_STAMP_PRESENT) {
			CodecHelper.encodeMillisUTC(millisUTC, zone, buffer);
		}
	}

	static final byte[] read(final ByteBuffer buffer, final byte marker) {
		final byte[] source = buffer.array();
		final int start = buffer.position();
		int index = start;
		while (index < source.length && source[index] != marker) {
			index++;
		}
		buffer.position(index + 1);
		final int size = index - start;
		final byte[] target = new byte[size];
		System.arraycopy(source, start, target, 0, size);
		return target;
	}

	static final byte find(final ByteBuffer buffer, final byte marker) {
		return find(buffer.array(), buffer.position(), marker);
	}

	static final byte find(final byte[] source, final int start,
			final byte marker) {
		int index = start;
		while (index < source.length && source[index] != marker) {
			index++;
		}
		if (index == start || index == source.length) {
			return NUL;
		} else {
			return source[index - 1];
		}
	}

	//

	final static void checkDigit(final int value) {
		if (value < 0 || value >= 10) {
			throw new IllegalArgumentException();
		}
	}

	final static void encodeUnsigned_1(final int value, final ByteBuffer buffer) {
		checkDigit(value);
		buffer.put((byte) (_0_ + value));
	}

	final static void encodeUnsigned_1_book(final int value,
			final ByteBuffer buffer) {
		if (0 <= value && value < 10) {
			buffer.put((byte) (_0_ + value));
			return;
		}
		if (10 <= value && value < (_Z_ - _A_)) {
			buffer.put((byte) (_A_ + value));
			return;
		}
		// FIXME silent error
		buffer.put(_0_);
	}

	final static byte decodeUnsigned_1(final ByteBuffer buffer) {
		final int value = buffer.get() - _0_;
		checkDigit(value);
		return (byte) value;
	}

	final static byte decodeUnsigned_1_book(final ByteBuffer buffer) {
		final byte alpha = buffer.get();
		if (isDigit(alpha)) {
			return (byte) (alpha - _0_);
		}
		if (isLetterUpper(alpha)) {
			return (byte) (10 + alpha - _A_);
		}
		// FIXME silent error
		return 0;
	}

	final static void encodeUnsigned_2(final int value, final ByteBuffer buffer) {
		if (value < 0 || value >= 100) {
			throw new IllegalArgumentException();
		}
		final int ones = value % 10;
		final int tens = value / 10;
		buffer.put((byte) (_0_ + tens)); // first byte
		buffer.put((byte) (_0_ + ones)); // second byte
	}

	final static byte decodeUnsigned_2(final ByteBuffer buffer) {
		final int tens = buffer.get() - _0_;
		checkDigit(tens);
		final int ones = buffer.get() - _0_;
		checkDigit(ones);
		return (byte) (tens * 10 + ones);
	}

	static final int bookBidIndexFrom(final byte code) {
		if (code < _K_ || _T_ < code) {
			throw new IllegalArgumentException();
		}
		return (code - _K_);
	}

	static final byte bookBidCodeFrom(final int index) {
		if (index < 0 || DDF_BOOK_LIMIT <= index) {
			throw new IllegalArgumentException();
		}
		return (byte) (_K_ + index);
	}

	static final int bookAskIndexFrom(final byte code) {
		if (code < _A_ || _J_ < code) {
			throw new IllegalArgumentException();
		}
		return (_J_ - code);
	}

	static final byte bookAskCodeFrom(final int index) {
		if (index < 0 || DDF_BOOK_LIMIT <= index) {
			throw new IllegalArgumentException();
		}
		return (byte) (_J_ - index);
	}

	static final byte[][] xmlDecSymbol(final Element tag,
			final String attribute, final boolean isThrow) {

		final String string = tag.getAttribute(attribute);
		if (string.length() > 0) {
			try {
				return DDF_Symbology.symbolArrayFromSymbolString(string);
			} catch (final Exception e) {
				// below
			}
		}
		if (isThrow) {
			throw new IllegalArgumentException("attribute not valid : "
					+ attribute);
		}
		return null;
	}

	// byte array

	static final boolean isXmlBook(final Element root) {
		return HelperXML.isXmlNameMatch(root, XmlTagBook.TAG);
	}

	static final boolean isXmlCuvol(final Element root) {
		return HelperXML.isXmlNameMatch(root, XmlTagCuvol.TAG);
	}

	static final boolean isXmlQuote(final Element root) {
		return HelperXML.isXmlNameMatch(root, XmlTagQuote.TAG);
	}

	final static byte encodeTimeStampByte(final int timeField) {
		return (byte) (timeField | DDF_TIME_STAMP_MASK);
	}

	final static int decodeTimeStampByte(final byte timeByte) {
		return timeByte & (~DDF_TIME_STAMP_MASK);
	}

	/** time zone information is discarded */
	static final void encodeTimeStamp(final ReadableDateTime dateTime,
			final ByteBuffer buffer) {

		// base fields
		buffer.put(DDF_CENTURY); // century
		buffer.put(encodeTimeStampByte(dateTime.getYearOfCentury())); // year
		buffer.put(encodeTimeStampByte(dateTime.getMonthOfYear())); // month
		buffer.put(encodeTimeStampByte(dateTime.getDayOfMonth())); // day
		buffer.put(encodeTimeStampByte(dateTime.getHourOfDay())); // hours
		buffer.put(encodeTimeStampByte(dateTime.getMinuteOfHour())); // minutes
		buffer.put(encodeTimeStampByte(dateTime.getSecondOfMinute())); // seconds

		// milliseconds
		final int millisOfSecond = dateTime.getMillisOfSecond();
		buffer.put((byte) (millisOfSecond & 0xFF)); // low byte
		buffer.put((byte) ((millisOfSecond >>> 8) & 0xFF)); // high byte

	}

	/** time zone information is provided */
	static final DateTime decodeTimeStamp(final DateTimeZone zone,
			final ByteBuffer buffer) {

		// base fields
		buffer.get(); // DDF_CENTURY
		final int yearOfEra = 2000 + decodeTimeStampByte(buffer.get());
		final int monthOfYear = decodeTimeStampByte(buffer.get());
		final int dayOfMonth = decodeTimeStampByte(buffer.get());
		final int hourOfDay = decodeTimeStampByte(buffer.get());
		final int minuteOfHour = decodeTimeStampByte(buffer.get());
		final int secondOfMinute = decodeTimeStampByte(buffer.get());

		// milliseconds
		final byte lo = buffer.get();
		final byte hi = buffer.get();
		final int millisOfSecond = ((hi & 0xFF) << 8) | (lo & 0xFF);

		// will throw RTE if any field is out of range
		return new DateTime(//
				yearOfEra, monthOfYear, dayOfMonth, //
				hourOfDay, minuteOfHour, secondOfMinute, //
				millisOfSecond, zone);

	}

	static final void encodeMillisUTC(final long millisUTC,
			final DateTimeZone zone, final ByteBuffer buffer) {
		final DateTime dateTime = new DateTime(millisUTC, zone);
		encodeTimeStamp(dateTime, buffer);
	}

	static final long decodeMillisUTC(final DateTimeZone zone,
			final ByteBuffer buffer) {
		final DateTime dateTime = decodeTimeStamp(zone, buffer);
		return dateTime.getMillis();
	}

	//

	static final void check(final byte left, final byte right) {
		if (left == right) {
			return;
		} else {
			throw new RuntimeException("no match;" + " left=" + left
					+ " right=" + right);
		}
	}

}




© 2015 - 2024 Weber Informatics LLC | Privacy Policy