(maxPayload, maxFrames int, withSnapshot bool)
| 292 | } |
| 293 | |
| 294 | func (s *Session) drainTx(maxPayload, maxFrames int, withSnapshot bool) ([]*frame.Frame, *DrainSnapshot) { |
| 295 | s.mu.Lock() |
| 296 | defer s.mu.Unlock() |
| 297 | |
| 298 | var snap *DrainSnapshot |
| 299 | if withSnapshot { |
| 300 | snap = &DrainSnapshot{ |
| 301 | synNeeded: s.synNeeded, |
| 302 | txBuf: s.txBuf, |
| 303 | txSeq: s.txSeq, |
| 304 | finSent: s.finSent, |
| 305 | finSentAt: s.finSentAt, |
| 306 | firstQueuedAt: s.firstQueuedAt, |
| 307 | } |
| 308 | } |
| 309 | |
| 310 | if !s.synNeeded && len(s.txBuf) == 0 && !(s.closeReq && !s.finSent) { |
| 311 | return nil, nil |
| 312 | } |
| 313 | |
| 314 | // Estimate capacity up front to avoid repeated slice growth under large |
| 315 | // uploads/downloads that split into many payload chunks. |
| 316 | estFrames := 0 |
| 317 | if s.synNeeded { |
| 318 | estFrames++ |
| 319 | } |
| 320 | if len(s.txBuf) > 0 { |
| 321 | if maxPayload <= 0 { |
| 322 | maxPayload = len(s.txBuf) |
| 323 | } |
| 324 | // First data chunk may ride on SYN, so payload-only frame count is |
| 325 | // bounded by ceil(len(txBuf)/maxPayload). |
| 326 | estFrames += (len(s.txBuf) + maxPayload - 1) / maxPayload |
| 327 | } |
| 328 | if s.closeReq && !s.finSent { |
| 329 | estFrames++ |
| 330 | } |
| 331 | if maxFrames > 0 && estFrames > maxFrames { |
| 332 | estFrames = maxFrames |
| 333 | } |
| 334 | frames := make([]*frame.Frame, 0, estFrames) |
| 335 | |
| 336 | canAppend := func() bool { |
| 337 | return maxFrames <= 0 || len(frames) < maxFrames |
| 338 | } |
| 339 | |
| 340 | // SYN (possibly with first chunk of payload). |
| 341 | if s.synNeeded && canAppend() { |
| 342 | f := &frame.Frame{ |
| 343 | SessionID: s.ID, |
| 344 | Seq: s.txSeq, |
| 345 | Flags: frame.FlagSYN, |
| 346 | Target: s.Target, |
| 347 | } |
| 348 | s.txSeq++ |
| 349 | s.synNeeded = false |
| 350 | if len(s.txBuf) > 0 { |
| 351 | n := len(s.txBuf) |
no test coverage detected