github.com/linapex/ethereum-go-chinese@v0.0.0-20190316121929-f8b7a73c3fa1/common/bitutil/compress.go (about)

     1  
     2  //<developer>
     3  //    <name>linapex 曹一峰</name>
     4  //    <email>linapex@163.com</email>
     5  //    <wx>superexc</wx>
     6  //    <qqgroup>128148617</qqgroup>
     7  //    <url>https://jsq.ink</url>
     8  //    <role>pku engineer</role>
     9  //    <date>2019-03-16 19:16:33</date>
    10  //</624450072834215936>
    11  
    12  
    13  package bitutil
    14  
    15  import "errors"
    16  
    17  var (
    18  //如果引用的字节
    19  //位集头溢出输入数据。
    20  	errMissingData = errors.New("missing bytes on input")
    21  
    22  //如果没有使用所有字节,则从解压返回errunreferenceddata。
    23  //在对输入数据进行解压缩之后。
    24  	errUnreferencedData = errors.New("extra bytes on input")
    25  
    26  //如果位集头具有
    27  //定义的比特数多于可用的目标缓冲区空间数。
    28  	errExceededTarget = errors.New("target data size exceeded")
    29  
    30  //如果中引用了数据字节,则从解压缩返回errZeroContent。
    31  //位集头实际上是一个零字节。
    32  	errZeroContent = errors.New("zero byte in input content")
    33  )
    34  
    35  //由compressBytes和compressBytes实现的压缩算法是
    36  //针对包含大量零字节的稀疏输入数据进行了优化。减压
    37  //需要解压数据长度的知识。
    38  //
    39  //压缩工程如下:
    40  //
    41  //如果数据只包含零,
    42  //compressbytes(data)==nil
    43  //否则,如果len(data)<=1,
    44  //compressBytes(data)==数据
    45  //否则:
    46  //compressbytes(data)==附加(compressbytes(nonzerobitset(data)),nonzerobytes(data)…)
    47  //哪里
    48  //非零位集(data)是一个带有len(data)位(msb first)的位向量:
    49  //非零位集(数据)[I/8]&&(1<(7-I%8))!=0,如果数据[i]!= 0
    50  //len(非零位集(数据))==(len(数据)+7)/8
    51  //非零字节(数据)包含相同顺序的非零字节数据
    52  
    53  //compressBytes根据稀疏位集压缩输入字节片
    54  //表示算法。如果结果大于原始输入,则不
    55  //压缩完成。
    56  func CompressBytes(data []byte) []byte {
    57  	if out := bitsetEncodeBytes(data); len(out) < len(data) {
    58  		return out
    59  	}
    60  	cpy := make([]byte, len(data))
    61  	copy(cpy, data)
    62  	return cpy
    63  }
    64  
    65  //bitsetEncodeBytes根据稀疏数据压缩输入字节片
    66  //位集表示算法。
    67  func bitsetEncodeBytes(data []byte) []byte {
    68  //空切片压缩为零
    69  	if len(data) == 0 {
    70  		return nil
    71  	}
    72  //单字节片压缩为零或保留单字节
    73  	if len(data) == 1 {
    74  		if data[0] == 0 {
    75  			return nil
    76  		}
    77  		return data
    78  	}
    79  //计算集合字节的位集,并收集非零字节
    80  	nonZeroBitset := make([]byte, (len(data)+7)/8)
    81  	nonZeroBytes := make([]byte, 0, len(data))
    82  
    83  	for i, b := range data {
    84  		if b != 0 {
    85  			nonZeroBytes = append(nonZeroBytes, b)
    86  			nonZeroBitset[i/8] |= 1 << byte(7-i%8)
    87  		}
    88  	}
    89  	if len(nonZeroBytes) == 0 {
    90  		return nil
    91  	}
    92  	return append(bitsetEncodeBytes(nonZeroBitset), nonZeroBytes...)
    93  }
    94  
    95  //解压缩字节用已知的目标大小解压缩数据。如果输入数据
    96  //匹配目标的大小,这意味着在第一个压缩过程中没有进行压缩
    97  //地点。
    98  func DecompressBytes(data []byte, target int) ([]byte, error) {
    99  	if len(data) > target {
   100  		return nil, errExceededTarget
   101  	}
   102  	if len(data) == target {
   103  		cpy := make([]byte, len(data))
   104  		copy(cpy, data)
   105  		return cpy, nil
   106  	}
   107  	return bitsetDecodeBytes(data, target)
   108  }
   109  
   110  //bitsetdecodebytes用已知的目标大小解压缩数据。
   111  func bitsetDecodeBytes(data []byte, target int) ([]byte, error) {
   112  	out, size, err := bitsetDecodePartialBytes(data, target)
   113  	if err != nil {
   114  		return nil, err
   115  	}
   116  	if size != len(data) {
   117  		return nil, errUnreferencedData
   118  	}
   119  	return out, nil
   120  }
   121  
   122  //BitsetDecodePartialBytes以已知的目标大小解压缩数据,但确实如此
   123  //不强制使用所有输入字节。除了减压
   124  //输出,函数返回相应的压缩输入数据的长度
   125  //因为输入片可能更长。
   126  func bitsetDecodePartialBytes(data []byte, target int) ([]byte, int, error) {
   127  //健全性检查0个目标以避免无限递归
   128  	if target == 0 {
   129  		return nil, 0, nil
   130  	}
   131  //处理零和单字节角情况
   132  	decomp := make([]byte, target)
   133  	if len(data) == 0 {
   134  		return decomp, 0, nil
   135  	}
   136  	if target == 1 {
   137  decomp[0] = data[0] //复制以避免引用输入切片
   138  		if data[0] != 0 {
   139  			return decomp, 1, nil
   140  		}
   141  		return decomp, 0, nil
   142  	}
   143  //解压集合字节的位集并分配非零字节
   144  	nonZeroBitset, ptr, err := bitsetDecodePartialBytes(data, (target+7)/8)
   145  	if err != nil {
   146  		return nil, ptr, err
   147  	}
   148  	for i := 0; i < 8*len(nonZeroBitset); i++ {
   149  		if nonZeroBitset[i/8]&(1<<byte(7-i%8)) != 0 {
   150  //确保我们有足够的数据插入正确的插槽
   151  			if ptr >= len(data) {
   152  				return nil, 0, errMissingData
   153  			}
   154  			if i >= len(decomp) {
   155  				return nil, 0, errExceededTarget
   156  			}
   157  //确保数据有效并推入插槽
   158  			if data[ptr] == 0 {
   159  				return nil, 0, errZeroContent
   160  			}
   161  			decomp[i] = data[ptr]
   162  			ptr++
   163  		}
   164  	}
   165  	return decomp, ptr, nil
   166  }
   167