|
|
// Copyright 2019, Chef. All rights reserved.
|
|
|
// https://github.com/q191201771/naza
|
|
|
//
|
|
|
// Use of this source code is governed by a MIT-style license
|
|
|
// that can be found in the License file.
|
|
|
//
|
|
|
// Author: Chef (191201771@qq.com)
|
|
|
|
|
|
package ic
|
|
|
|
|
|
import (
|
|
|
"encoding/binary"
|
|
|
)
|
|
|
|
|
|
type LFCompressor struct {
|
|
|
FB uint32 // 用几个字节的 bit 表示跟随的数据
|
|
|
ZlibExt bool // 压缩之后,是否再用 zlib 进一步压缩
|
|
|
|
|
|
oc OriginCompressor // FB 为0时,退化成使用 OriginCompressor
|
|
|
}
|
|
|
|
|
|
// 传入的整型切片必须是从小到大有序排列
|
|
|
func (lfc *LFCompressor) Marshal(ids []uint32) (ret []byte) {
|
|
|
if lfc.FB == 0 {
|
|
|
ret = lfc.oc.Marshal(ids)
|
|
|
if lfc.ZlibExt {
|
|
|
ret = zlibWrite(ret)
|
|
|
}
|
|
|
return ret
|
|
|
}
|
|
|
|
|
|
lBuf := make([]byte, 4)
|
|
|
fBuf := make([]byte, lfc.FB)
|
|
|
|
|
|
maxDiff := 8 * lfc.FB
|
|
|
|
|
|
var hasLeader bool
|
|
|
var leader uint32
|
|
|
var stage int
|
|
|
for i := range ids {
|
|
|
if !hasLeader {
|
|
|
stage = 1
|
|
|
leader = ids[i]
|
|
|
hasLeader = true
|
|
|
continue
|
|
|
}
|
|
|
|
|
|
diff := uint32(ids[i] - leader)
|
|
|
|
|
|
if diff > maxDiff {
|
|
|
binary.LittleEndian.PutUint32(lBuf, leader)
|
|
|
ret = append(ret, lBuf...)
|
|
|
ret = append(ret, fBuf...)
|
|
|
|
|
|
resetBuf(fBuf)
|
|
|
stage = 2
|
|
|
leader = ids[i]
|
|
|
} else {
|
|
|
stage = 3
|
|
|
fBuf[(diff-1)/8] = fBuf[(diff-1)/8] | (1 << byte((diff-1)%8))
|
|
|
}
|
|
|
}
|
|
|
|
|
|
switch stage {
|
|
|
case 1:
|
|
|
binary.LittleEndian.PutUint32(lBuf, leader)
|
|
|
ret = append(ret, lBuf...)
|
|
|
dummy := make([]byte, lfc.FB)
|
|
|
ret = append(ret, dummy...)
|
|
|
case 2:
|
|
|
binary.LittleEndian.PutUint32(lBuf, leader)
|
|
|
ret = append(ret, lBuf...)
|
|
|
dummy := make([]byte, lfc.FB)
|
|
|
ret = append(ret, dummy...)
|
|
|
case 3:
|
|
|
binary.LittleEndian.PutUint32(lBuf, leader)
|
|
|
ret = append(ret, lBuf...)
|
|
|
ret = append(ret, fBuf...)
|
|
|
}
|
|
|
if lfc.ZlibExt {
|
|
|
ret = zlibWrite(ret)
|
|
|
}
|
|
|
return
|
|
|
}
|
|
|
|
|
|
func (lfc *LFCompressor) Unmarshal(b []byte) (ids []uint32) {
|
|
|
if lfc.ZlibExt {
|
|
|
b = zlibRead(b)
|
|
|
}
|
|
|
if lfc.FB == 0 {
|
|
|
return lfc.oc.Unmarshal(b)
|
|
|
}
|
|
|
|
|
|
isLeaderStage := true
|
|
|
var item uint32
|
|
|
var leader uint32
|
|
|
var index uint32
|
|
|
for {
|
|
|
if isLeaderStage {
|
|
|
leader = binary.LittleEndian.Uint32(b[index:])
|
|
|
ids = append(ids, leader)
|
|
|
isLeaderStage = false
|
|
|
index += 4
|
|
|
} else {
|
|
|
for i := uint32(0); i < lfc.FB; i++ {
|
|
|
for j := uint32(0); j < 8; j++ {
|
|
|
if ((b[index+i] >> j) & 1) == 1 {
|
|
|
item = leader + (i * 8) + j + 1
|
|
|
ids = append(ids, item)
|
|
|
}
|
|
|
}
|
|
|
}
|
|
|
|
|
|
isLeaderStage = true
|
|
|
index += lfc.FB
|
|
|
}
|
|
|
|
|
|
if int(index) == len(b) {
|
|
|
break
|
|
|
}
|
|
|
}
|
|
|
return
|
|
|
}
|