scala位压缩与行情转换二进制
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位压缩与行情转换二进制的更多相关文章
- Java 进制转换(二进制(负),八进制,十进制,十六进制),位运算、逻辑运算(2)
负数的二进制表现形式:其实就是该数的绝对值取反+1. 进制转换(二进制,八进制,十进制,十六进制),原理解析 十六进制的表现形式: (2)(与.异或.左移.右移.三元运算符)
- Formiko总结整数十进制转换二进制原理
引子: 为什么十进制转二进制的“辗转相除记录余数倒序输出”的算法是正确的?这个问题陪伴了Formiko半年. 实践: 实践一:把十进制数100转换成二进制数的图 上图和和下图唯一的区别在最后一位上 ...
- C#由转换二进制所引起的思考,了解下?
前言 最近遇到很有意思转换二进制的问题,有部分童鞋俨然已了解,可能也有一部分童鞋没碰到过也就不知情,这里我们来深入学习下转换二进制所带来的问题. 二进制转换问题 假设现在我们有一个int类型的数据,它 ...
- POJ 1753. Flip Game 枚举or爆搜+位压缩,或者高斯消元法
Flip Game Time Limit: 1000MS Memory Limit: 65536K Total Submissions: 37427 Accepted: 16288 Descr ...
- NowCoder猜想(素数筛法+位压缩)
在期末被各科的大作业碾压快要窒息之际,百忙之中抽空上牛客网逛了逛,无意中发现一道好题,NowCoder猜想,题意很明显,就是个简单的素数筛法,但竟然超内存了,我晕(+﹏+)~ 明明有 3 万多 k ...
- python 调用第三方库压缩png或者转换成webp
因为工作需要去研究了下png的压缩,发现转换成webp可以小很多,但是webp在手机上的解码速度比png的解码速度慢很多.出于进几年手机设备的处理器的性能也不错了,所以准备两套方案. 在网上搜索了一些 ...
- Python 进制转换 二进制 八进制 十进制 十六进制
Python 进制转换 二进制 八进制 十进制 十六进制 作者:方倍工作室 地址:http://www.cnblogs.com/txw1958/p/python3-scale.html 全局定义一定不 ...
- scala 时间,时间格式转换
scala 时间,时间格式转换 1.scala 时间格式转换(String.Long.Date) 1.1时间字符类型转Date类型 1.2Long类型转字符类型 1.3时间字符类型转Long类型 2. ...
- python的进制转换二进制,八进制,十六进制及其原理
#!usr/bin/env python# coding:utf-8def binary(): '''二进制的方法与算法''' Number = 10 Number1 = 20 Nu ...
随机推荐
- MySQL (八)-- 事务、变量、触发器
1 事务 需求:有一张银行账户表,A用户给B用户转账,A账户先减少,B账户增加,但是A操作完之后断电了. 解决方案:A减少钱,但是不要立即修改数据表,B收到钱之后,同时修改数据表. 事务:一系列要发生 ...
- python--购物车优化
基本要求:用户入口1.商品信息存在文件里 2.已购商品.余额记录,长期保存, 即第一次启动是需要输入预算的,以后就拿剩下的钱买东西 商家入口 1.可以添加商品,删除商品,修改商品价格 2.按q可以退出 ...
- 集美大学网络1413第十五次作业成绩(团队十) -- 项目复审与事后分析(Beta版本)
题目 团队作业10--项目复审与事后分析(Beta版本) 团队作业10成绩 --团队作业10-1 Beta事后诸葛亮 团队/分值 设想和目标 计划 资源 变更管理 设计/实现 测试/发布 团队的角色 ...
- Java中的基本数据类型和基本数据类型之间的转换
在Java中有8中基本数据类型,分别为: 整型: byte.short.int.long 浮点型:float.double 布尔型:boolean 字符型:char. byte: 8位, 封装 ...
- 201521123073 《Java程序设计》第13周学习总结
1. 13周学习总结 以你喜欢的方式(思维导图.OneNote或其他)归纳总结多网络相关内容. 2. 书面作业 1. 网络基础 1.1 比较ping www.baidu.com与ping cec.jm ...
- 201521123026《JAVA程序设计》第11周学习总结
1. 本章学习总结 1.1 以你喜欢的方式(思维导图或其他)归纳总结多线程相关内容. 1.多线程同步:限制某个资源在同一时刻只能被一个线程访问.. 2.同步代码块:`synchronized(lock ...
- 201521123027 <java程序设计>第九周学习总结
1.本周学习总结 1.1 以你喜欢的方式(思维导图或其他)归纳总结异常相关内容. 2.书面作业 Q1.常用异常 题目5-1 1.1 截图你的提交结果(出现学号) 1.2 自己以前编写的代码中经常出现什 ...
- 201521123118《java与程序设计》第14周学习总结
1. 本周学习总结 1.1 以你喜欢的方式(思维导图或其他)归纳总结多数据库相关内容. 2. 书面作业 1. MySQL数据库基本操作 建立数据库,将自己的姓名.学号作为一条记录插入.(截图,需出现自 ...
- HashMap 学习心得
1.构造 HashMap 底层数据结构线性数组,HashMap有一个静态内部类Entry,Entry有四个属性,key,value,next,hash Entry就是HashMap键值对实现的一个基础 ...
- python之socket编程------粘包
一.粘包 什么是粘包 只有TCP只有粘包现象,UDP永远不会粘包 所谓粘包问题主要还是因为接收方不知道之间的界限,不知道一次性提取多少字节的数据所造成的 两种情况发生粘包: 1.发送端需要等缓冲区满才 ...