github.com/m3db/m3@v1.5.1-0.20231129193456-75a402aa583b/src/dbnode/ts/segment.go (about) 1 // Copyright (c) 2016 Uber Technologies, Inc. 2 // 3 // Permission is hereby granted, free of charge, to any person obtaining a copy 4 // of this software and associated documentation files (the "Software"), to deal 5 // in the Software without restriction, including without limitation the rights 6 // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 7 // copies of the Software, and to permit persons to whom the Software is 8 // furnished to do so, subject to the following conditions: 9 // 10 // The above copyright notice and this permission notice shall be included in 11 // all copies or substantial portions of the Software. 12 // 13 // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 14 // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 15 // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 16 // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 17 // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 18 // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 19 // THE SOFTWARE. 20 21 package ts 22 23 import ( 24 "bytes" 25 26 "github.com/m3db/m3/src/x/checked" 27 "github.com/m3db/m3/src/x/pool" 28 29 "github.com/m3db/stackadler32" 30 ) 31 32 // Segment represents a binary blob consisting of two byte slices and 33 // declares whether they should be finalized when the segment is finalized. 34 type Segment struct { 35 // Head is the head of the segment. 36 Head checked.Bytes 37 // Tail is the tail of the segment. 38 Tail checked.Bytes 39 // SegmentFlags declares whether to finalize when finalizing the segment. 40 Flags SegmentFlags 41 // checksum is the checksum for the segment. 42 checksum uint32 43 } 44 45 // SegmentFlags describes the option to finalize or not finalize 46 // bytes in a Segment. 47 type SegmentFlags uint8 48 49 const ( 50 // FinalizeNone specifies to finalize neither of the bytes 51 FinalizeNone SegmentFlags = 1 << 0 52 // FinalizeHead specifies to finalize the head bytes 53 FinalizeHead SegmentFlags = 1 << 1 54 // FinalizeTail specifies to finalize the tail bytes 55 FinalizeTail SegmentFlags = 1 << 2 56 ) 57 58 // CalculateChecksum calculates and sets the 32-bit checksum for 59 // this segment avoiding any allocations. 60 func (s *Segment) CalculateChecksum() uint32 { 61 if s.checksum != 0 { 62 return s.checksum 63 } 64 d := stackadler32.NewDigest() 65 if s.Head != nil { 66 d = d.Update(s.Head.Bytes()) 67 } 68 if s.Tail != nil { 69 d = d.Update(s.Tail.Bytes()) 70 } 71 s.checksum = d.Sum32() 72 return s.checksum 73 } 74 75 // NewSegment will create a new segment and increment the refs to 76 // head and tail if they are non-nil. When finalized the segment will 77 // also finalize the byte slices if FinalizeBytes is passed. 78 func NewSegment( 79 head, tail checked.Bytes, 80 checksum uint32, 81 flags SegmentFlags, 82 ) Segment { 83 if head != nil { 84 head.IncRef() 85 } 86 if tail != nil { 87 tail.IncRef() 88 } 89 return Segment{ 90 Head: head, 91 Tail: tail, 92 Flags: flags, 93 checksum: checksum, 94 } 95 } 96 97 // Len returns the length of the head and tail. 98 func (s *Segment) Len() int { 99 var total int 100 if s.Head != nil { 101 total += s.Head.Len() 102 } 103 if s.Tail != nil { 104 total += s.Tail.Len() 105 } 106 return total 107 } 108 109 // Equal returns if this segment is equal to another. 110 // WARNING: This should only be used in code paths not 111 // executed often as it allocates bytes to concat each 112 // segment head and tail together before comparing the contents. 113 func (s *Segment) Equal(other *Segment) bool { 114 var head, tail, otherHead, otherTail []byte 115 if s.Head != nil { 116 head = s.Head.Bytes() 117 } 118 if s.Tail != nil { 119 tail = s.Tail.Bytes() 120 } 121 if other.Head != nil { 122 otherHead = other.Head.Bytes() 123 } 124 if other.Tail != nil { 125 otherTail = other.Tail.Bytes() 126 } 127 return bytes.Equal(append(head, tail...), append(otherHead, otherTail...)) 128 } 129 130 // Finalize will finalize the segment by decrementing refs to head and 131 // tail if they are non-nil. 132 func (s *Segment) Finalize() { 133 if s.Head != nil { 134 s.Head.DecRef() 135 if s.Flags&FinalizeHead == FinalizeHead { 136 s.Head.Finalize() 137 } 138 } 139 s.Head = nil 140 if s.Tail != nil { 141 s.Tail.DecRef() 142 if s.Flags&FinalizeTail == FinalizeTail { 143 s.Tail.Finalize() 144 } 145 } 146 s.Tail = nil 147 } 148 149 // Clone will create a copy of this segment with an optional bytes pool. 150 func (s *Segment) Clone(pool pool.CheckedBytesPool) Segment { 151 var ( 152 checkedHead, checkedTail checked.Bytes 153 tail []byte 154 ) 155 156 head := s.Head.Bytes() 157 if s.Tail != nil { 158 tail = s.Tail.Bytes() 159 } 160 161 if pool != nil { 162 checkedHead = pool.Get(len(head)) 163 checkedHead.IncRef() 164 checkedHead.AppendAll(head) 165 checkedHead.DecRef() 166 167 if tail != nil { 168 checkedTail = pool.Get(len(tail)) 169 checkedTail.IncRef() 170 checkedTail.AppendAll(tail) 171 checkedTail.DecRef() 172 } 173 } else { 174 ch := make([]byte, len(head)) 175 copy(ch, head) 176 checkedHead = checked.NewBytes(ch, nil) 177 178 if tail != nil { 179 ct := make([]byte, len(tail)) 180 copy(ct, tail) 181 checkedTail = checked.NewBytes(ct, nil) 182 } 183 } 184 185 // NB: new segment is always finalizeable. 186 return NewSegment(checkedHead, checkedTail, 187 s.CalculateChecksum(), FinalizeHead&FinalizeTail) 188 }