github.com/datastax/go-cassandra-native-protocol@v0.0.0-20220706104457-5e8aad05cf90/segment/encode.go (about) 1 // Copyright 2020 DataStax 2 // 3 // Licensed under the Apache License, Version 2.0 (the "License"); 4 // you may not use this file except in compliance with the License. 5 // You may obtain a copy of the License at 6 // 7 // http://www.apache.org/licenses/LICENSE-2.0 8 // 9 // Unless required by applicable law or agreed to in writing, software 10 // distributed under the License is distributed on an "AS IS" BASIS, 11 // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 // See the License for the specific language governing permissions and 13 // limitations under the License. 14 15 package segment 16 17 import ( 18 "bytes" 19 "encoding/binary" 20 "fmt" 21 "io" 22 23 "github.com/datastax/go-cassandra-native-protocol/crc" 24 ) 25 26 // MaxPayloadLength is the maximum payload length a Segment can contain. Since the payload length header field contains 27 // 17 bits, the maximum payload length is 2ˆ17-1 = 131,071. 28 const MaxPayloadLength = 131_071 29 30 func (c *codec) EncodeSegment(segment *Segment, dest io.Writer) error { 31 payloadLength := len(segment.Payload.UncompressedData) 32 segment.Header.UncompressedPayloadLength = int32(payloadLength) 33 if payloadLength > MaxPayloadLength { 34 return fmt.Errorf("paload length exceeds maximum allowed: %v > %v", payloadLength, MaxPayloadLength) 35 } else { 36 if c.compressor == nil { 37 return c.encodeSegmentUncompressed(segment, dest) 38 } else { 39 return c.encodeSegmentCompressed(segment, dest) 40 } 41 } 42 } 43 44 func (c *codec) encodeSegmentUncompressed(segment *Segment, dest io.Writer) error { 45 segment.Header.CompressedPayloadLength = 0 46 segment.Payload.Crc32 = crc.ChecksumIEEE(segment.Payload.UncompressedData) 47 if err := c.encodeHeaderUncompressed(segment.Header, dest); err != nil { 48 return fmt.Errorf("cannot encode segment header: %w", err) 49 } else if _, err := dest.Write(segment.Payload.UncompressedData); err != nil { 50 return fmt.Errorf("cannot write encoded segment payload: %w", err) 51 } else if err := c.writePayloadCrc(segment.Payload.Crc32, dest); err != nil { 52 return fmt.Errorf("cannot write encoded segment payload CRC: %w", err) 53 } 54 return nil 55 } 56 57 func (c *codec) encodeSegmentCompressed(segment *Segment, dest io.Writer) error { 58 uncompressedPayload := bytes.NewBuffer(segment.Payload.UncompressedData) 59 compressedPayload := bytes.NewBuffer(make([]byte, 0, len(segment.Payload.UncompressedData))) 60 if err := c.compressor.Compress(uncompressedPayload, compressedPayload); err != nil { 61 return fmt.Errorf("cannot compress segment payload: %w", err) 62 } else { 63 var payload *bytes.Buffer 64 segment.Header.CompressedPayloadLength = int32(compressedPayload.Len()) 65 if segment.Header.CompressedPayloadLength <= segment.Header.UncompressedPayloadLength { 66 payload = compressedPayload 67 } else { 68 // compression is not worth it 69 payload = uncompressedPayload 70 segment.Header.CompressedPayloadLength = segment.Header.UncompressedPayloadLength 71 segment.Header.UncompressedPayloadLength = 0 72 } 73 segment.Payload.Crc32 = crc.ChecksumIEEE(payload.Bytes()) 74 if err := c.encodeHeaderCompressed(segment.Header, dest); err != nil { 75 return fmt.Errorf("cannot encode segment header: %w", err) 76 } else if _, err := payload.WriteTo(dest); err != nil { 77 return fmt.Errorf("cannot write encoded segment payload: %w", err) 78 } else if err := c.writePayloadCrc(segment.Payload.Crc32, dest); err != nil { 79 return fmt.Errorf("cannot write encoded segment payload CRC: %w", err) 80 } 81 return nil 82 } 83 } 84 85 func (c *codec) encodeHeaderUncompressed(header *Header, dest io.Writer) error { 86 const headerLength = UncompressedHeaderLength 87 const flagOffset = 17 88 headerData := uint64(header.UncompressedPayloadLength) 89 if header.IsSelfContained { 90 headerData |= 1 << flagOffset 91 } 92 return c.writeHeaderDataAndCrc(headerData, headerLength, dest) 93 } 94 95 func (c *codec) encodeHeaderCompressed(header *Header, dest io.Writer) error { 96 const headerLength = CompressedHeaderLength 97 const flagOffset = 34 98 headerData := uint64(header.CompressedPayloadLength) 99 headerData |= uint64(header.UncompressedPayloadLength) << 17 100 if header.IsSelfContained { 101 headerData |= 1 << flagOffset 102 } 103 return c.writeHeaderDataAndCrc(headerData, headerLength, dest) 104 } 105 106 func (c *codec) writeHeaderDataAndCrc(headerData uint64, headerLength int, dest io.Writer) error { 107 headerCrc := crc.ChecksumKoopman(headerData, headerLength) 108 for i := 0; i < headerLength; i++ { 109 if err := binary.Write(dest, binary.LittleEndian, (byte)(headerData)); err != nil { 110 return fmt.Errorf("cannot write encoded segment header data: %w", err) 111 } 112 headerData >>= 8 113 } 114 for i := 0; i < Crc24Length; i++ { 115 if err := binary.Write(dest, binary.LittleEndian, (byte)(headerCrc)); err != nil { 116 return fmt.Errorf("cannot write encoded segment header CRC: %w", err) 117 } 118 headerCrc >>= 8 119 } 120 return nil 121 } 122 123 func (c *codec) writePayloadCrc(payloadCrc uint32, dest io.Writer) error { 124 if err := binary.Write(dest, binary.LittleEndian, payloadCrc); err != nil { 125 return fmt.Errorf("cannot write encoded segment payload CRC: %w", err) 126 } 127 return nil 128 }