github.com/bodgit/sevenzip@v1.5.1/internal/bra/bcj.go (about)

     1  package bra
     2  
     3  import (
     4  	"encoding/binary"
     5  	"io"
     6  )
     7  
     8  const bcjLookAhead = 4
     9  
    10  type bcj struct {
    11  	ip, state uint32
    12  }
    13  
    14  func (c *bcj) Size() int { return bcjLookAhead + 1 }
    15  
    16  func test86MSByte(b byte) bool {
    17  	return (b+1)&0xfe == 0
    18  }
    19  
    20  //nolint:cyclop,funlen,gocognit
    21  func (c *bcj) Convert(b []byte, encoding bool) int {
    22  	if len(b) < c.Size() {
    23  		return 0
    24  	}
    25  
    26  	var (
    27  		pos  int
    28  		mask = c.state & 7
    29  	)
    30  
    31  	for {
    32  		p := pos
    33  		for ; p < len(b)-bcjLookAhead; p++ {
    34  			if b[p]&0xfe == 0xe8 {
    35  				break
    36  			}
    37  		}
    38  
    39  		d := p - pos
    40  		pos = p
    41  
    42  		if p >= len(b)-bcjLookAhead {
    43  			if d > 2 {
    44  				c.state = 0
    45  			} else {
    46  				c.state = mask >> d
    47  			}
    48  
    49  			c.ip += uint32(pos)
    50  
    51  			return pos
    52  		}
    53  
    54  		if d > 2 {
    55  			mask = 0
    56  		} else {
    57  			mask >>= d
    58  			if mask != 0 && (mask > 4 || mask == 3 || test86MSByte(b[p+int(mask>>1)+1])) {
    59  				mask = (mask >> 1) | 4
    60  				pos++
    61  
    62  				continue
    63  			}
    64  		}
    65  
    66  		//nolint:nestif
    67  		if test86MSByte(b[p+4]) {
    68  			v := binary.LittleEndian.Uint32(b[p+1:])
    69  			cur := c.ip + uint32(c.Size()+pos)
    70  			pos += c.Size()
    71  
    72  			if encoding {
    73  				v += cur
    74  			} else {
    75  				v -= cur
    76  			}
    77  
    78  			if mask != 0 {
    79  				sh := mask & 6 << 2
    80  				if test86MSByte(byte(v >> sh)) {
    81  					v ^= (uint32(0x100) << sh) - 1
    82  					if encoding {
    83  						v += cur
    84  					} else {
    85  						v -= cur
    86  					}
    87  				}
    88  
    89  				mask = 0
    90  			}
    91  
    92  			binary.LittleEndian.PutUint32(b[p+1:], v)
    93  			b[p+4] = 0 - b[p+4]&1
    94  		} else {
    95  			mask = (mask >> 1) | 4
    96  			pos++
    97  		}
    98  	}
    99  }
   100  
   101  // NewBCJReader returns a new BCJ io.ReadCloser.
   102  func NewBCJReader(_ []byte, _ uint64, readers []io.ReadCloser) (io.ReadCloser, error) {
   103  	return newReader(readers, new(bcj))
   104  }