import org.jboss.netty.buffer.{ChannelBuffers, ChannelBuffer}
import java.nio.charset.Charset
import BigDecimal.RoundingMode._
/*
* 采用LittleEndian字节顺序。结构为控制字节+附加字节
* 控制字节
* 7 符号位
* 6-4 数据字节数量,在实际数据字节中保存原始数据的绝对值
* 3-0 特定小整数保留位,0-15的数字可以不使用数据字节直接在控制字节中表示
*
* 范围截断。由于控制字节的限制,最多附加4bit + 7个字节(即60bit)的数据,如果超过范围,则进行截断。
*/ class BitCompress(buf: ChannelBuffer) {
def compressLong(in: Long) {
//抽取符号位、如果为负值则取绝对值,同时进行范围截断
var (control, abs) = {
val MaxAbsValue = 0x0fffffffffffffffl //60bit
if (in < 0) (0x80.toByte, (-in) & MaxAbsValue)
else (0x00.toByte, in & MaxAbsValue)
} //附加数据缓冲区
val additionalBuffer = ChannelBuffers.buffer(8) //去除待压缩数据中多余的0
var additionalLength = 0 //为方便,将控制字节中的附加数据也作为一个附加数据处理,存储附加数据个数时应减1
if (abs == 0) additionalLength += 1 //0无法进入else中的循环,无法为附加数据长度赋值,因而特殊处理
else {
val BytesOfLong = 8
for (i <- 1 to BytesOfLong) {
val dataByte = ((abs >>> (BytesOfLong - i) * 8) & 0xff).toByte
if ((additionalLength != 0) || (dataByte != 0)) {
//有效数据开始:附加数据不为空或者字节数据不为0 if (additionalLength != 0) {
additionalBuffer.writeByte(dataByte)
additionalLength += 1
}
else {
//附加数据为空,有可能在控制字节中写入数据
//高4位有数据,为偶数个4bit,控制字节低4bit不用存放数据
//高4位没有数据,为奇数个4bit,控制字节低4bit需要存放数据
if ((dataByte & 0xf0) != 0) {
additionalBuffer.writeByte(dataByte)
additionalLength += 2
}
else {
control = (control | dataByte).toByte
additionalLength += 1
}
}
}
}
} //附加数据长度
additionalLength -= 1 //为方便,将控制字节中的附加数据也作为一个附加数据处理,存储附加数据个数时应减1
control = (control | (additionalLength << 4)).toByte //写入结果
buf.writeByte(control)
if (additionalLength != 0) buf.writeBytes(additionalBuffer)
} //压缩数据
def <-<(in: Byte) { compressLong(in.toLong) }
def <-<(in: Short) { compressLong(in.toLong) }
def <-<(in: Int) { compressLong(in.toLong)}
def <-<(in: Long) { compressLong(in) } //压缩BigDecimal数据
def <-<(in: BigDecimal) {
compressLong(in.setScale(0, HALF_UP).toLong)
} //压缩字符串(以0为结尾标志)
def compressString(bytes: Array[Byte]) {
buf.writeBytes(bytes)
buf.writeByte(0)
} def <-<(bytes: Array[Byte]) { compressString(bytes) } //写入数据
def <--(in: Byte) { buf.writeByte(in) }
def <--(in: Short) { buf.writeShort(in) }
def <--(in: Int) { buf.writeInt(in) }
def <--(in: Long) { buf.writeLong(in) }
def <--(in: Float) { buf.writeFloat(in) }
def <--(in: Double) { buf.writeDouble(in) } //解压Long数据
def decompressLong() = {
//控制字节
//7 符号位
//6-4 数据字节数,数据字节中保存原始数据的绝对值
//3-0 特定小整数保留位,0-15的数字可以不使用数据字节直接在控制字节中表示
val control = buf.readByte()
val negative = (control & 0x80) == 0x80 //是否为负数 //附加数据长度
val additionalLength = (control & 0x70) >>> 4 //控制字节所包含的附加数据
var result: Long = control.toLong & 0x0f //解压
for (i <- 1 to additionalLength) {
result <<= 8
result |= (buf.readByte().toLong & 0xffL) //byte转为Long时,如为负数则默认填充位为1
} //符号
if (negative) result = -result result
} def decompressString(charsetName: String = "GBK") = {
val dup = buf.duplicate() var length = 0
var done = false
while (dup.readable() && !done) {
if (dup.readByte() != 0) length += 1
else done = true
} val result = buf.readBytes(length).toString(Charset.forName(charsetName))
buf.skipBytes(1) result
} def readByte() = buf.readByte()
def readShort() = buf.readShort()
def readInt() = buf.readInt()
def readLong() = buf.readLong()
def readFloat() = buf.readFloat()
def readDouble() = buf.readDouble()
}

行情二进制写

import java.nio.ByteOrder
import java.util import org.jboss.netty.buffer.ChannelBuffers class SnapshotBuffer() { def read(bytes: Array[Byte]): Snapshot = {
null
} def write(snap: Snapshot): Array[Byte] = {
val bufferSize = 1024 * 1024
val buffer = ChannelBuffers.dynamicBuffer(ByteOrder.LITTLE_ENDIAN, bufferSize)
val bc = new BitCompress(buffer)
val points = 10000 bc.compressString(snap.from.getBytes("UTF-8"))
bc <-- snap.totalSn
bc <-- snap.exchangeSn
bc <-- snap.varietySn val year = snap.dateTime.getYear
val month = snap.dateTime.getMonthOfYear
val day = snap.dateTime.getDayOfMonth
val date = year * 10000 + month * 100 + day
val hour = snap.dateTime.getHourOfDay
val minute = snap.dateTime.getMinuteOfHour
val second = snap.dateTime.getSecondOfMinute
val mmsec = hour * 10000 + minute * 100 + second bc.compressLong(date)
bc.compressLong(mmsec)
bc.compressLong(snap.exchangeType.id)
bc.compressLong(snap.varietyType.id)
bc.compressLong(snap.marketStatus.id)
bc.compressLong(snap.snapshots.size) snap.snapshots.foreach { item =>
bc <-< 4 //不压缩
bc <-- (item.dateTime.getMillis / 1000).toInt
bc <-- item.marketId.id.toShort //字符串以0结束
bc.compressString(item.symbol.getBytes("UTF-8"))
bc.compressString(item.name.getBytes("UTF-8")) bc <-< item.prevClose * points
bc <-< (item.open - item.prevClose) * points
bc <-< (item.high - item.prevClose) * points
bc <-< (item.low - item.prevClose) * points
bc <-< (item.close - item.prevClose) * points //不压缩
bc <-- item.volume.toFloat
bc <-- item.amount.toFloat //保留2位压缩
bc <-< item.pe * 100 val bids = Array.fill(5)(new OrderBook(0, 0))
val asks = Array.fill(5)(new OrderBook(0, 0)) item.bids.toArray.copyToArray(bids)
item.asks.toArray.copyToArray(asks) val buyPrice = bids(0).price
bc <-< buyPrice * points
bc <-< (bids(1).price - buyPrice) * points
bc <-< (bids(2).price - buyPrice) * points
bc <-< (bids(3).price - buyPrice) * points
bc <-< (bids(4).price - buyPrice) * points bc <-< bids(0).lots
bc <-< bids(1).lots
bc <-< bids(2).lots
bc <-< bids(3).lots
bc <-< bids(4).lots bc <-< (asks(0).price - buyPrice) * points
bc <-< (asks(1).price - buyPrice) * points
bc <-< (asks(2).price - buyPrice) * points
bc <-< (asks(3).price - buyPrice) * points
bc <-< (asks(4).price - buyPrice) * points bc <-< asks(0).lots
bc <-< asks(1).lots
bc <-< asks(2).lots
bc <-< asks(3).lots
bc <-< asks(4).lots bc <-< item.tradeLots
bc <-< {
if (item.suspended) 1 else 0
}
bc <-< item.holds
bc <-< 0
} util.Arrays.copyOf(buffer.array, buffer.writerIndex) }
}

其中copyOf方法是将buffer复制到一个新数组,但是把多分配的size删除。

行情二进制的抽取

object SnapshotWrapper {

  val defaultTimeZone = DateTimeZone.forID("Asia/Shanghai")
def unapply(binary: Array[Byte]): Option[Snapshot] = {
val c = new BitCompress(ChannelBuffers.wrappedBuffer(ByteOrder.LITTLE_ENDIAN, binary)) val from = c.decompressString("UTF-8")
val sn = c.readLong() val dateTime = {
val date = c.decompressLong().toInt
val time = c.decompressLong().toInt DateTimeFormat.forPattern("yyyyMMddHHmmssZ").withOffsetParsed().parseDateTime(
f"""$date%08d$time%06d+0800"""
)
} val marketId = MarketId(c.decompressLong().toInt) //9:25 - 9:30 之间市场标志已经处于连续交易状态,但实际市场处于连续交易之前的准备阶段
//特殊处理一下,将9:30之前的连续交易状态处理成集合竞价状态
val marketStatus = {
val cur = MarketStatus(c.decompressLong().toInt) val before930 = dateTime.getHourOfDay == 9 &&
dateTime.getMinuteOfHour < 30 val before9 = dateTime.getHourOfDay < 9 import MarketStatus._
if (
cur == ContinueTrade && (before930 || before9)
) {
Auction } else {
cur
}
} val count = c.decompressLong().toInt val buf = new ListBuffer[SnapshotItem]
for (i <- 1 to count) {
val divider = if (c.decompressLong() == 4) 10000 else 1000 //价格除数 val time = new DateTime(c.readInt()*1000L, defaultTimeZone) //date time val marketId = MarketId(c.readShort())
val symbol = c.decompressString()
val cnName = c.decompressString() val prevClose = BigDecimal(c.decompressLong()) / divider
val open = (BigDecimal(c.decompressLong()) / divider) + prevClose
val high = (BigDecimal(c.decompressLong()) / divider) + prevClose
val low = (BigDecimal(c.decompressLong()) / divider) + prevClose
val close = (BigDecimal(c.decompressLong()) / divider) + prevClose val volume = BigDecimal(c.readFloat())
val amount = BigDecimal(c.readFloat()) val pe = BigDecimal(c.decompressLong()) / 100 //order books
val (bids, asks) = orderBooks(c, divider) val tradeLots = c.decompressLong() //成交笔数 val suspended = if (c.decompressLong() == 0) false else true //停牌
val holds = c.decompressLong() //持仓
c.decompressLong() //unused buf += SnapshotItem(
time,
marketId, symbol, cnName,
prevClose, open, high, low, close, volume, amount,
pe, bids, asks,
tradeLots, suspended, holds
)
} Some(Snapshot(
from,
sn,
dateTime,
marketId, marketStatus,
buf.toList
))
} /**
* 优化OrderBook性能
*
* order book结构如下
* bids: price1 ... price5 lots1 ... lots5
* asks: price1 ... price5 lots1 ... lots5
*/
@inline
private def orderBooks(c: BitCompress, divider: Int) = {
val base = c.decompressLong() val bidsPrice = Array(
base,
c.decompressLong() + base,
c.decompressLong() + base,
c.decompressLong() + base,
c.decompressLong() + base
) val bidsLots = Array(
c.decompressLong(),
c.decompressLong(),
c.decompressLong(),
c.decompressLong(),
c.decompressLong()
) val asksPrice = Array(
c.decompressLong() + base,
c.decompressLong() + base,
c.decompressLong() + base,
c.decompressLong() + base,
c.decompressLong() + base
) val asksLots = Array(
c.decompressLong(),
c.decompressLong(),
c.decompressLong(),
c.decompressLong(),
c.decompressLong()
) var bids: List[OrderBook] = Nil
for (i <- 0 to 4) {
if (bidsPrice(i) != 0) {
bids = OrderBook(
BigDecimal(bidsPrice(i)) / divider,
BigDecimal(bidsLots(i))
) :: bids
}
} var asks: List[OrderBook] = Nil
for (i <- 0 to 4) {
if (asksPrice(i) != 0) {
asks = OrderBook(
BigDecimal(asksPrice(i)) / divider,
BigDecimal(asksLots(i))
) :: asks
}
} (bids.reverse, asks.reverse)
}
}

scala位压缩与行情转换二进制的更多相关文章

  1. Java 进制转换(二进制(负),八进制,十进制,十六进制),位运算、逻辑运算(2)

    负数的二进制表现形式:其实就是该数的绝对值取反+1. 进制转换(二进制,八进制,十进制,十六进制),原理解析 十六进制的表现形式: (2)(与.异或.左移.右移.三元运算符)

  2. Formiko总结整数十进制转换二进制原理

    引子: 为什么十进制转二进制的“辗转相除记录余数倒序输出”的算法是正确的?这个问题陪伴了Formiko半年. 实践: 实践一:把十进制数100转换成二进制数的图   上图和和下图唯一的区别在最后一位上 ...

  3. C#由转换二进制所引起的思考,了解下?

    前言 最近遇到很有意思转换二进制的问题,有部分童鞋俨然已了解,可能也有一部分童鞋没碰到过也就不知情,这里我们来深入学习下转换二进制所带来的问题. 二进制转换问题 假设现在我们有一个int类型的数据,它 ...

  4. POJ 1753. Flip Game 枚举or爆搜+位压缩,或者高斯消元法

    Flip Game Time Limit: 1000MS   Memory Limit: 65536K Total Submissions: 37427   Accepted: 16288 Descr ...

  5. NowCoder猜想(素数筛法+位压缩)

    在期末被各科的大作业碾压快要窒息之际,百忙之中抽空上牛客网逛了逛,无意中发现一道好题,NowCoder猜想,题意很明显,就是个简单的素数筛法,但竟然超内存了,我晕(+﹏+)~  明明有 3 万多 k ...

  6. python 调用第三方库压缩png或者转换成webp

    因为工作需要去研究了下png的压缩,发现转换成webp可以小很多,但是webp在手机上的解码速度比png的解码速度慢很多.出于进几年手机设备的处理器的性能也不错了,所以准备两套方案. 在网上搜索了一些 ...

  7. Python 进制转换 二进制 八进制 十进制 十六进制

    Python 进制转换 二进制 八进制 十进制 十六进制 作者:方倍工作室 地址:http://www.cnblogs.com/txw1958/p/python3-scale.html 全局定义一定不 ...

  8. scala 时间,时间格式转换

    scala 时间,时间格式转换 1.scala 时间格式转换(String.Long.Date) 1.1时间字符类型转Date类型 1.2Long类型转字符类型 1.3时间字符类型转Long类型 2. ...

  9. python的进制转换二进制,八进制,十六进制及其原理

    #!usr/bin/env python# coding:utf-8def binary(): '''二进制的方法与算法'''    Number = 10    Number1 = 20    Nu ...

随机推荐

  1. idea 给maven项目添加依赖(二)

    这里接着上一篇来 我们观察目录发现有两个pom.xml(project object module) 项目是里面的,所以外面的先不管它. 点击里面的pom.xml 1.在<url>节点下面 ...

  2. 自制OSPF配置实验大全

    OSPF综合实验(gns模拟器)   注意点: Ospf实验推荐用gns制作 //图中的交换机可以视觉忽略或缩小成一个点.(^ v^) Ospf 实验拓扑图 ​ 目录 OSPF综合实验... 1 Os ...

  3. ★浅谈Spanking情节

  4. dnsmasq一次成功的配置

    第一次用这个小软件,感觉还不错,因为没有像bind那样配置起来繁琐,并且我们也不需要去配置很多文件,内外网访问互不干涉. 我是在centos6.5下进行配置的: 先说说自己的理解: dnsmasq先去 ...

  5. bean的生命周期以及延迟实例化

    可以指定bean的初始化创建的时候调用的方法,以及销毁的时候调用的方法. 通过指定中的init-method和destroy-method方法指定bean的创建和销毁的时候执行类中的方法. 把lazy ...

  6. 201521123101 《Java程序设计》第6周学习总结

    1. 本周学习总结 1.面向对象学习暂告一段落,请使用思维导图,以封装.继承.多态为核心概念画一张思维导图,对面向对象思想进行一个总结. 2. 书面作业 1.clone方法 1.1 Object对象中 ...

  7. 201521123105《jave程序》第二周学习总结

    1. 本周学习总结 学习了各种java数据类型以及各种运算符的使用 学习了一维,二维数组的用法 学习了String类对象使用 2. 书面作业 使用Eclipse关联jdk源代码,并查看String对象 ...

  8. 201521123093 java 第十三周学习总结

    1. 本周学习总结 以你喜欢的方式(思维导图.OneNote或其他)归纳总结多网络相关内容. 2. 书面作业 1. 网络基础 1.1 比较ping www.baidu.com与ping cec.jmu ...

  9. ActiveMQ_Windows版本的安装部署

    1, 保证电脑上安装了jdk6以上版本的java,并配置了好环境变量 : 2, 官方下载地址:http://activemq.apache.org/download-archives.html ,这里 ...

  10. 这是一名Java学者关于学习方向的建议

    无可厚非,编程是一门艺术.但是辉煌的背后必须是一段辛苦的奋斗过程,而过程的引导方向就是最重要的一环.Java语言可谓是引领了编程的潮流,你会是怎样去学的呢? 这是一名Java学者的学习方向的建议 注: ...