package lz4
import (
"errors"
"io"
"github.com/pierrec/lz4/v4/internal/lz4block"
"github.com/pierrec/lz4/v4/internal/lz4errors"
"github.com/pierrec/lz4/v4/internal/lz4stream"
)
type crState int
const (
crStateInitial crState = iota
crStateReading
crStateFlushing
crStateDone
)
type CompressingReader struct {
state crState
src io .ReadCloser
level lz4block .CompressionLevel
frame *lz4stream .Frame
in []byte
out ovWriter
handler func (int )
}
func NewCompressingReader (src io .ReadCloser ) *CompressingReader {
zrd := &CompressingReader {
frame : lz4stream .NewFrame (),
}
_ = zrd .Apply (DefaultBlockSizeOption , DefaultChecksumOption , defaultOnBlockDone )
zrd .Reset (src )
return zrd
}
func (zrd *CompressingReader ) Source () io .ReadCloser {
return zrd .src
}
func (zrd *CompressingReader ) Close () error {
return zrd .src .Close ()
}
func (zrd *CompressingReader ) Apply (options ...Option ) (err error ) {
if zrd .state != crStateInitial {
return lz4errors .ErrOptionClosedOrError
}
zrd .Reset (zrd .src )
for _ , o := range options {
if err = o (zrd ); err != nil {
return
}
}
return
}
func (*CompressingReader ) private () {}
func (zrd *CompressingReader ) init () error {
zrd .frame .InitW (&zrd .out , 1 , false )
size := zrd .frame .Descriptor .Flags .BlockSizeIndex ()
zrd .in = size .Get ()
return zrd .frame .Descriptor .Write (zrd .frame , &zrd .out )
}
func (zrd *CompressingReader ) Read (p []byte ) (n int , err error ) {
defer func () {
if err != nil {
zrd .state = crStateDone
}
}()
if !zrd .out .reset (p ) {
return len (p ), nil
}
switch zrd .state {
case crStateInitial :
err = zrd .init ()
if err != nil {
return
}
zrd .state = crStateReading
case crStateDone :
return 0 , errors .New ("This reader is done" )
case crStateFlushing :
if zrd .out .dataPos > 0 {
n = zrd .out .dataPos
zrd .out .data = nil
zrd .out .dataPos = 0
return
} else {
zrd .state = crStateDone
return 0 , io .EOF
}
}
for zrd .state == crStateReading {
block := zrd .frame .Blocks .Block
var rCount int
rCount , err = io .ReadFull (zrd .src , zrd .in )
switch err {
case nil :
err = block .Compress (
zrd .frame , zrd .in [ : rCount ], zrd .level ,
).Write (zrd .frame , &zrd .out )
zrd .handler (len (block .Data ))
if err != nil {
return
}
if zrd .out .dataPos == len (zrd .out .data ) {
n = zrd .out .dataPos
zrd .out .dataPos = 0
zrd .out .data = nil
return
}
case io .EOF , io .ErrUnexpectedEOF :
if rCount > 0 {
err = block .Compress (
zrd .frame , zrd .in [ : rCount ], zrd .level ,
).Write (zrd .frame , &zrd .out )
zrd .handler (len (block .Data ))
if err != nil {
return
}
}
err = zrd .frame .CloseW (&zrd .out , 1 )
if err != nil {
return
}
zrd .state = crStateFlushing
n = zrd .out .dataPos
zrd .out .dataPos = 0
zrd .out .data = nil
return
default :
return
}
}
err = lz4errors .ErrInternalUnhandledState
return
}
func (zrd *CompressingReader ) Reset (src io .ReadCloser ) {
zrd .frame .Reset (1 )
zrd .state = crStateInitial
zrd .src = src
zrd .out .clear ()
}
type ovWriter struct {
data []byte
ov []byte
dataPos int
ovPos int
}
func (wr *ovWriter ) Write (p []byte ) (n int , err error ) {
count := copy (wr .data [wr .dataPos : ], p )
wr .dataPos += count
if count < len (p ) {
wr .ov = append (wr .ov , p [count : ]...)
}
return len (p ), nil
}
func (wr *ovWriter ) reset (out []byte ) bool {
ovRem := len (wr .ov ) - wr .ovPos
if ovRem >= len (out ) {
wr .ovPos += copy (out , wr .ov [wr .ovPos : ])
return false
}
if ovRem > 0 {
copy (out , wr .ov [wr .ovPos : ])
wr .ov = wr .ov [ : 0 ]
wr .ovPos = 0
wr .dataPos = ovRem
} else if wr .ovPos > 0 {
wr .ov = wr .ov [ : 0 ]
wr .ovPos = 0
wr .dataPos = 0
}
wr .data = out
return true
}
func (wr *ovWriter ) clear () {
wr .data = nil
wr .dataPos = 0
wr .ov = wr .ov [ : 0 ]
wr .ovPos = 0
}
The pages are generated with Golds v0.8.2 . (GOOS=linux GOARCH=amd64)
Golds is a Go 101 project developed by Tapir Liu .
PR and bug reports are welcome and can be submitted to the issue list .
Please follow @zigo_101 (reachable from the left QR code) to get the latest news of Golds .