go.chromium.org/luci@v0.0.0-20240309015107-7cdc2e660f33/logdog/client/butler/bundler/datagramParser.go (about) 1 // Copyright 2015 The LUCI Authors. 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 bundler 16 17 import ( 18 "io" 19 20 "go.chromium.org/luci/common/data/recordio" 21 "go.chromium.org/luci/logdog/api/logpb" 22 ) 23 24 // datagramParser is a parser implementation for the LogDog datagram stream 25 // type. 26 type datagramParser struct { 27 baseParser 28 29 // maxSize is the maximum allowed datagram size. Datagrams larger than this 30 // will result in a processing error. 31 maxSize int64 32 33 // seq is the current datagram sequence number. 34 seq int64 35 36 // remaining is the amount of data remaining in a datagram that has previously 37 // been emitted partially. 38 // 39 // This will be zero if we're not continuing a partial datagram. 40 remaining int64 41 // index is the index of this datagram. This is zero unless the datagram is 42 // a continuation of a previous partial datagram, in which case this is the 43 // continuation's index. 44 index int64 45 // size is the size of the current partial datagram. 46 // 47 // This value is only valid if we're continuing a partial datagram (i.e., if 48 // remaining is non-zero). 49 size int64 50 } 51 52 var _ parser = (*datagramParser)(nil) 53 54 func (s *datagramParser) nextEntry(c *constraints) (*logpb.LogEntry, error) { 55 // Use the current Buffer timestamp. 56 ts, has := s.firstChunkTime() 57 if !has { 58 // No chunks, so no data. 59 return nil, nil 60 } 61 62 // If remaining is zero, we don't have a buffered size header. 63 // 64 // Note that zero-size datagrams will store zero here on load; however, such 65 // datagrams will never fail to emit a LogEntry, so s.remaining will have been 66 // reset to zero by the next call. 67 if s.remaining == 0 { 68 bv := s.View() 69 70 // Read the next datagram size header. 71 rio := recordio.NewReader(bv, s.maxSize) 72 size, _, err := rio.ReadFrame() 73 if err != nil { 74 switch err { 75 case io.EOF, io.ErrUnexpectedEOF: 76 // Not enough data for a size header. 77 return nil, nil 78 79 case recordio.ErrFrameTooLarge: 80 return nil, recordio.ErrFrameTooLarge 81 } 82 // Other errors should not be possible, since all operations are against 83 // in-memory buffers. 84 memoryCorruption(err) 85 } 86 87 s.index = 0 88 s.size = size 89 s.remaining = size 90 91 // Don't need to read the size header again. 92 s.Consume(bv.Consumed()) 93 } 94 95 // If we read this, will it be partial? 96 emitCount := s.remaining 97 continued := false 98 if emitCount > int64(c.limit) { 99 continued = true 100 emitCount = int64(c.limit) 101 } 102 103 bv := s.ViewLimit(s.remaining) 104 if r := bv.Remaining(); r < emitCount { 105 // Not enough buffered data to complete the datagram in one round. 106 continued = true 107 emitCount = r 108 } 109 if s.remaining > 0 && emitCount == 0 { 110 // The datagram has data, but we can't emit any of it. No point in issuing 111 // a zero-size partial datagram. 112 return nil, nil 113 } 114 115 // We're not willing to emit a partial datagram unless we're allowed to 116 // split. 117 if continued && !c.allowSplit { 118 return nil, nil 119 } 120 121 dg := logpb.Datagram{} 122 if continued || s.index > 0 { 123 dg.Partial = &logpb.Datagram_Partial{ 124 Index: uint32(s.index), 125 Size: uint64(s.size), 126 Last: !continued, 127 } 128 } 129 if emitCount > 0 { 130 dg.Data = make([]byte, emitCount) 131 bv.Read(dg.Data) 132 s.Consume(emitCount) 133 } 134 135 le := s.baseLogEntry(ts) 136 le.Sequence = uint64(s.seq) 137 le.Content = &logpb.LogEntry_Datagram{Datagram: &dg} 138 139 if !continued { 140 s.seq++ 141 s.remaining = 0 142 // Will reset remaining partial fields on next read, since remaining == 0. 143 } else { 144 s.index++ 145 s.remaining -= emitCount 146 } 147 return le, nil 148 }