ReadStringInto reads a string-typed reply directly into buf, avoiding the per-call allocation that ReadString incurs. It returns the number of bytes written to buf. Supported reply types: - $ \r\n \r\n bulk string (the GET path; payload is read straight into buf via bufio.Reader — for pa
(buf []byte)
| 558 | // / float responses the payload lives in the (already-consumed) header |
| 559 | // line, so no drain is needed. |
| 560 | func (r *Reader) ReadStringInto(buf []byte) (int, error) { |
| 561 | line, err := r.ReadLine() |
| 562 | if err != nil { |
| 563 | return 0, err |
| 564 | } |
| 565 | |
| 566 | switch line[0] { |
| 567 | case RespStatus: |
| 568 | // Simple string — data is in the line itself. |
| 569 | s := line[1:] |
| 570 | if len(s) > len(buf) { |
| 571 | return 0, fmt.Errorf("redis: buffer too small: need %d bytes, have %d", len(s), len(buf)) |
| 572 | } |
| 573 | return copy(buf, s), nil |
| 574 | |
| 575 | case RespString: |
| 576 | n, err := replyLen(line) |
| 577 | if err != nil { |
| 578 | return 0, err |
| 579 | } |
| 580 | if n > len(buf) { |
| 581 | // Drain the payload + trailing \r\n so the next read on this |
| 582 | // connection sees the start of the next reply rather than the |
| 583 | // tail of this one. Otherwise the unread bytes corrupt the |
| 584 | // stream and the bad connection gets handed back to the pool. |
| 585 | if _, derr := r.rd.Discard(n + 2); derr != nil { |
| 586 | return 0, derr |
| 587 | } |
| 588 | return 0, fmt.Errorf("redis: buffer too small: need %d bytes, have %d", n, len(buf)) |
| 589 | } |
| 590 | // Read data directly into the user's buffer through the bufio.Reader. |
| 591 | // bufio.Reader.Read first drains its internal buffer, then for |
| 592 | // remaining data larger than its buffer size reads directly from the |
| 593 | // underlying reader (socket) — effectively zero-copy. |
| 594 | if _, err := io.ReadFull(r.rd, buf[:n]); err != nil { |
| 595 | return 0, err |
| 596 | } |
| 597 | // Discard trailing \r\n. |
| 598 | if _, err := r.rd.Discard(2); err != nil { |
| 599 | return 0, err |
| 600 | } |
| 601 | return n, nil |
| 602 | |
| 603 | case RespInt, RespFloat: |
| 604 | s := line[1:] |
| 605 | if len(s) > len(buf) { |
| 606 | return 0, fmt.Errorf("redis: buffer too small: need %d bytes, have %d", len(s), len(buf)) |
| 607 | } |
| 608 | return copy(buf, s), nil |
| 609 | } |
| 610 | |
| 611 | return 0, fmt.Errorf("redis: can't parse reply=%.100q reading string into buffer", line) |
| 612 | } |
| 613 | |
| 614 | func (r *Reader) ReadString() (string, error) { |
| 615 | line, err := r.ReadLine() |