// Package lz4stream provides the types that support reading and writing LZ4 data streams.
package lz4stream import ( ) //go:generate go run gen.go const ( frameMagic uint32 = 0x184D2204 frameSkipMagic uint32 = 0x184D2A50 frameMagicLegacy uint32 = 0x184C2102 ) func () *Frame { return &Frame{} } type Frame struct { buf [15]byte // frame descriptor needs at most 4(magic)+4+8+1=11 bytes Magic uint32 Descriptor FrameDescriptor Blocks Blocks Checksum uint32 checksum xxh32.XXHZero } // Reset allows reusing the Frame. // The Descriptor configuration is not modified. func ( *Frame) ( int) { .Magic = 0 .Descriptor.Checksum = 0 .Descriptor.ContentSize = 0 _ = .Blocks.close(, ) .Checksum = 0 } func ( *Frame) ( io.Writer, int, bool) { if { .Magic = frameMagicLegacy := lz4block.Index(lz4block.Block8Mb) .Descriptor.Flags.BlockSizeIndexSet() } else { .Magic = frameMagic .Descriptor.initW() } .Blocks.initW(, , ) .checksum.Reset() } func ( *Frame) ( io.Writer, int) error { if := .Blocks.close(, ); != nil { return } if .isLegacy() { return nil } := .buf[:0] // End mark (data block size of uint32(0)). = append(, 0, 0, 0, 0) if .Descriptor.Flags.ContentChecksum() { = .checksum.Sum() } , := .Write() return } func ( *Frame) () bool { return .Magic == frameMagicLegacy } func ( *Frame) ( io.Reader) error { if .Magic > 0 { // Header already read. return nil } : var error if .Magic, = .readUint32(); != nil { return } switch := .Magic; { case == frameMagic || == frameMagicLegacy: // All 16 values of frameSkipMagic are valid. case >>8 == frameSkipMagic>>8: , := .readUint32() if != nil { return } if , := io.CopyN(ioutil.Discard, , int64()); != nil { return } goto default: return lz4errors.ErrInvalidFrame } if := .Descriptor.initR(, ); != nil { return } .checksum.Reset() return nil } func ( *Frame) ( io.Reader, int) (chan []byte, error) { return .Blocks.initR(, , ) } func ( *Frame) ( io.Reader) ( error) { if .isLegacy() { return nil } if !.Descriptor.Flags.ContentChecksum() { return nil } if .Checksum, = .readUint32(); != nil { return } if := .checksum.Sum32(); != .Checksum { return fmt.Errorf("%w: got %x; expected %x", lz4errors.ErrInvalidFrameChecksum, , .Checksum) } return nil } type FrameDescriptor struct { Flags DescriptorFlags ContentSize uint64 Checksum uint8 } func ( *FrameDescriptor) () { .Flags.VersionSet(1) .Flags.BlockIndependenceSet(true) } func ( *FrameDescriptor) ( *Frame, io.Writer) error { if .Checksum > 0 { // Header already written. return nil } := .buf[:4] // Write the magic number here even though it belongs to the Frame. binary.LittleEndian.PutUint32(, .Magic) if !.isLegacy() { = [:4+2] binary.LittleEndian.PutUint16([4:], uint16(.Flags)) if .Flags.Size() { = [:4+2+8] binary.LittleEndian.PutUint64([4+2:], .ContentSize) } .Checksum = descriptorChecksum([4:]) = append(, .Checksum) } , := .Write() return } func ( *FrameDescriptor) ( *Frame, io.Reader) error { if .isLegacy() { := lz4block.Index(lz4block.Block8Mb) .Descriptor.Flags.BlockSizeIndexSet() return nil } // Read the flags and the checksum, hoping that there is not content size. := .buf[:3] if , := io.ReadFull(, ); != nil { return } := binary.LittleEndian.Uint16() .Flags = DescriptorFlags() if .Flags.Size() { // Append the 8 missing bytes. = [:3+8] if , := io.ReadFull(, [3:]); != nil { return } .ContentSize = binary.LittleEndian.Uint64([2:]) } .Checksum = [len()-1] // the checksum is the last byte = [:len()-1] // all descriptor fields except checksum if := descriptorChecksum(); .Checksum != { return fmt.Errorf("%w: got %x; expected %x", lz4errors.ErrInvalidHeaderChecksum, , .Checksum) } // Validate the elements that can be. if := .Flags.BlockSizeIndex(); !.IsValid() { return lz4errors.ErrOptionInvalidBlockSize } return nil } func descriptorChecksum( []byte) byte { return byte(xxh32.ChecksumZero() >> 8) }