mirror of
https://gitee.com/willfree/min-dev-java.git
synced 2026-10-04 19:42:01 +08:00
323 lines
10 KiB
Java
323 lines
10 KiB
Java
package encoding;
|
|
|
|
/*
|
|
* @Author: Wang Feng
|
|
* @Description: 一个工具类,用来便捷的实现TLV编码
|
|
* @Version: 1.0.0
|
|
* @Date: 20:21 2021/3/5
|
|
* @Copyright: MIN-Group;国家重大科技基础设施——未来网络北大实验室;深圳市信息论与未来网络重点实验室
|
|
*/
|
|
public class Encoder {
|
|
//TODO 便于测试暂时先将private属性改成public了
|
|
public byte[] buffer;
|
|
public int left; // 左指针 => 指向 Prepend* 操作下一个可以插入的位置
|
|
public int right; // 右指针 => 指向 Append* 操作下一个可以插入的位置
|
|
public boolean isInitial; // 是否初始化
|
|
public boolean isEstimator;// 是否仅测量编码后的长度,而不实际进行编码
|
|
public SizeT totalReserve; // 编码缓存数组的大小
|
|
|
|
public static final int MaxPacketSize=8800;
|
|
|
|
/**
|
|
* 编码测量初始化
|
|
* @Description:
|
|
* 1. 一旦调用这个函数进行初始化,则调用所有的编码函数只会测量编码后的长度,不会执行实际的编码
|
|
*/
|
|
public void estimatorReset(){
|
|
this.left=0;
|
|
this.right=1;
|
|
this.isInitial=true;
|
|
this.isEstimator=true;
|
|
this.totalReserve=new SizeT(Long.MAX_VALUE);
|
|
}
|
|
|
|
/**
|
|
* 编码初始化
|
|
* @Description:
|
|
* @param totalReserve 总的存储编码结果的数组长度
|
|
* @param reserveFromBack 默认情况下,左右指针都指向数组的最右侧,表示totalReserve指定的所有空间都是预留给 Prepend* 操作的
|
|
* reserveFromBack 可以指定需要预留多少空间给 Append*操作
|
|
*/
|
|
public boolean encoderReset(SizeT totalReserve,SizeT reserveFromBack) throws EncoderException{
|
|
if(reserveFromBack.compareTo(totalReserve)>0){
|
|
throw new EncoderException("encoderReset: reserveFromBack > totalReserve");
|
|
}
|
|
this.isInitial=true;
|
|
this.isEstimator=false;
|
|
this.totalReserve=totalReserve;
|
|
int len=0;
|
|
try {
|
|
len=totalReserve.getVlIntValue2Int();
|
|
} catch (VlIntException e) {
|
|
throw new EncoderException("encoderReset: "+e.getMessage());
|
|
}
|
|
this.buffer=new byte[len]; //todo: 特殊情况下,可能越界。
|
|
this.left= (int) (totalReserve.subtract(reserveFromBack)-1);//todo: 特殊情况下,可能越界。
|
|
this.right= (int) totalReserve.subtract(reserveFromBack);//todo: 特殊情况下,可能越界。
|
|
return true;
|
|
}
|
|
|
|
/**
|
|
* 判断是否为空
|
|
* @return
|
|
*/
|
|
public boolean empty(){
|
|
return (this.left+1)>=this.right;
|
|
}
|
|
|
|
/**
|
|
* 编码后的大小
|
|
* @return
|
|
*/
|
|
public SizeT size(){
|
|
return new SizeT(this.right-this.left-1);
|
|
}
|
|
|
|
|
|
/**
|
|
* 在每个操作之前进行有效性检查
|
|
* @Description:
|
|
* 1. 首先检查 Encoder 实例有没有初始化,没有初始化则返回错误;
|
|
* 2. 然后检查预计新增 size 个字节,会不会超出最大包限制,超出则返回错误;
|
|
* 3. 否则返回 nil
|
|
* @param size
|
|
* @return
|
|
*/
|
|
public boolean check(SizeT size){
|
|
if(!this.isInitial){
|
|
return false;
|
|
}
|
|
if(this.totalReserve.compareTo(this.size().add(size))<0){
|
|
return false;
|
|
}
|
|
return true;
|
|
}
|
|
|
|
/**
|
|
* Prepend a byte
|
|
* @param value
|
|
* @return 返回成功插入的字节数
|
|
*/
|
|
public int prependByte(byte value){
|
|
if(this.isEstimator){
|
|
this.left--;
|
|
return 1;
|
|
}
|
|
if(check(new SizeT(1))){
|
|
this.buffer[this.left]=value;
|
|
this.left--;
|
|
return 1;
|
|
}else{
|
|
return -1;
|
|
}
|
|
}
|
|
|
|
/**
|
|
* @param value
|
|
* @return 返回插入成功的字节数
|
|
*/
|
|
public int appendByte(byte value){
|
|
if(this.isEstimator){
|
|
this.right++;
|
|
return 1;
|
|
}
|
|
if(check(new SizeT(1))){
|
|
this.buffer[this.right]=value;
|
|
this.right++;
|
|
return 1;
|
|
}else{
|
|
return -1;
|
|
}
|
|
}
|
|
|
|
|
|
/**
|
|
* 编码一个非负整数到一个数组当中
|
|
* todo: 越界问题
|
|
* @param uint64
|
|
* @return
|
|
*/
|
|
public static byte[] buildNonNegativeIntegerArr(long uint64){
|
|
VlInt vlInt=new VlInt(uint64);
|
|
int len=vlInt.getVlIntBytes().length;
|
|
if(uint64<=255){
|
|
return new byte[]{(byte) uint64};
|
|
}else {
|
|
byte[] res = new byte[len - 1];
|
|
System.arraycopy(vlInt.getVlIntBytes(),1,res,0,len-1);
|
|
return res;
|
|
}
|
|
}
|
|
|
|
/**
|
|
* 在前向插入一个非负整数,最大为 uint64::Max
|
|
* todo: 越界问题
|
|
* @param uint64_value
|
|
* @return
|
|
*/
|
|
public int prependNonNegativeInteger(long uint64_value) throws EncoderException {
|
|
byte[] bytes=buildNonNegativeIntegerArr(uint64_value);
|
|
return this.prependByteArray(bytes,new SizeT(bytes.length));
|
|
}
|
|
|
|
/**
|
|
* 在后向插入一个非负整数,最大为 uint64::Max
|
|
* todo: 越界问题
|
|
* @param uint64_value
|
|
* @return
|
|
*/
|
|
public int appendNonNegativeInteger(long uint64_value) throws EncoderException {
|
|
byte[] bytes=buildNonNegativeIntegerArr(uint64_value);
|
|
return this.appendByteArray(bytes,new SizeT(bytes.length));
|
|
}
|
|
|
|
/**
|
|
* @param array
|
|
* @param size
|
|
* @return 返回插入成功的字节数
|
|
*/
|
|
public int prependByteArray(byte[] array,SizeT size) throws EncoderException {
|
|
if(check(size)&&(size.compareTo(array.length)<=0)){
|
|
if(this.isEstimator){
|
|
this.left-=array.length;
|
|
try {
|
|
return size.getVlIntValue2Int();
|
|
} catch (VlIntException e) {
|
|
throw new EncoderException("prependByteArray: "+e.getMessage());
|
|
}
|
|
}else{
|
|
try {
|
|
System.arraycopy(array,0,this.buffer,
|
|
this.left-size.getVlIntValue2Int()+1,size.getVlIntValue2Int());
|
|
} catch (VlIntException e) {
|
|
throw new EncoderException("prependByteArray: "+e.getMessage());
|
|
}
|
|
this.left-=array.length;
|
|
try {
|
|
return size.getVlIntValue2Int();
|
|
} catch (VlIntException e) {
|
|
throw new EncoderException("prependByteArray: "+e.getMessage());
|
|
}
|
|
}
|
|
}
|
|
return -1;
|
|
}
|
|
|
|
/**
|
|
* @param array
|
|
* @param size
|
|
* @return 返回插入成功的字节数
|
|
*/
|
|
public int appendByteArray(byte[] array,SizeT size) throws EncoderException {
|
|
if(check(size)&&(size.compareTo(array.length)<=0)){
|
|
if(this.isEstimator){
|
|
this.right+=array.length;
|
|
try {
|
|
return size.getVlIntValue2Int();
|
|
} catch (VlIntException e) {
|
|
throw new EncoderException("appendByteArray: "+e.getMessage());
|
|
}
|
|
}else{
|
|
try {
|
|
System.arraycopy(array,0,this.buffer,
|
|
this.right,size.getVlIntValue2Int());
|
|
} catch (VlIntException e) {
|
|
throw new EncoderException("appendByteArray: "+e.getMessage());
|
|
}
|
|
this.right+=array.length;
|
|
try {
|
|
return size.getVlIntValue2Int();
|
|
} catch (VlIntException e) {
|
|
throw new EncoderException("appendByteArray: "+e.getMessage());
|
|
}
|
|
}
|
|
}
|
|
return -1;
|
|
}
|
|
|
|
/**
|
|
* 在前向插入一个可变长度的数字
|
|
* @param varNumber
|
|
* @return
|
|
*/
|
|
public int prependVarNumber(VlInt varNumber) throws EncoderException {
|
|
return this.prependByteArray(varNumber.getVlIntBytes(),new SizeT(varNumber.size()));
|
|
}
|
|
|
|
/**
|
|
* 在后向插入一个可变长度的数字
|
|
* @param varNumber
|
|
* @return
|
|
*/
|
|
public int appendVarNumber(VlInt varNumber) throws EncoderException {
|
|
return this.appendByteArray(varNumber.getVlIntBytes(),new SizeT(varNumber.size()));
|
|
}
|
|
|
|
//todo: 在前向&后向插入一个非负整数
|
|
|
|
/**
|
|
* 根据TLV-TYPE和一个字节数组,在前向插入一个 TLV Block
|
|
* @param tlvType
|
|
* @param array
|
|
* @param size
|
|
* @return
|
|
*/
|
|
public int prependByteArrayBlock(VlInt tlvType,byte[] array,SizeT size) throws EncoderException {
|
|
int totalLength=this.prependByteArray(array,size);
|
|
totalLength+=this.prependVarNumber(new VlInt(size));
|
|
totalLength+=this.prependVarNumber(tlvType);
|
|
return totalLength;
|
|
}
|
|
|
|
/**
|
|
* 根据TLV-TYPE和一个字节数组,在后向插入一个 TLV Block
|
|
* @param tlvType
|
|
* @param array
|
|
* @param size
|
|
* @return
|
|
*/
|
|
public int appendByteArrayBlock(VlInt tlvType,byte[] array,SizeT size) throws EncoderException {
|
|
int totalLength=0;
|
|
// Append TLV-TYPE
|
|
totalLength+=this.appendVarNumber(tlvType);
|
|
// Append TLV-LENGTH
|
|
totalLength+=this.appendVarNumber(new VlInt(size));
|
|
// Append TLV-VALUE
|
|
totalLength+=this.appendByteArray(array,size);
|
|
|
|
return totalLength;
|
|
}
|
|
|
|
/**
|
|
* 在前向插入一个 TLV Block
|
|
* @param block
|
|
* @return
|
|
*/
|
|
public int prependBlock(Block block) throws EncoderException {
|
|
return this.prependByteArrayBlock(block.getType(),block.getValue(),new SizeT(block.getLength()));
|
|
}
|
|
|
|
/**
|
|
* 在后向插入一个 TLV Block
|
|
* @param block
|
|
* @return
|
|
*/
|
|
public int appendBlock(Block block) throws EncoderException {
|
|
return this.appendByteArrayBlock(block.getType(),block.getValue(),new SizeT(block.getLength()));
|
|
}
|
|
|
|
/**
|
|
* 获取编码后的字节数组
|
|
* @return
|
|
*/
|
|
public byte[] getBuffer() {
|
|
if((!this.isInitial)||this.isEstimator){
|
|
return null;
|
|
}
|
|
int len=this.right-this.left-1;
|
|
byte[] res=new byte[len];
|
|
System.arraycopy(this.buffer,this.left+1,res,0,len);
|
|
return res;
|
|
}
|
|
}
|