stdcopy.go 3.6 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152
  1. package utils
  2. import (
  3. "encoding/binary"
  4. "errors"
  5. "io"
  6. )
  7. const (
  8. StdWriterPrefixLen = 8
  9. StdWriterFdIndex = 0
  10. StdWriterSizeIndex = 4
  11. )
  12. type StdType [StdWriterPrefixLen]byte
  13. var (
  14. Stdin StdType = StdType{0: 0}
  15. Stdout StdType = StdType{0: 1}
  16. Stderr StdType = StdType{0: 2}
  17. )
  18. type StdWriter struct {
  19. io.Writer
  20. prefix StdType
  21. sizeBuf []byte
  22. }
  23. func (w *StdWriter) Write(buf []byte) (n int, err error) {
  24. if w == nil || w.Writer == nil {
  25. return 0, errors.New("Writer not instanciated")
  26. }
  27. binary.BigEndian.PutUint32(w.prefix[4:], uint32(len(buf)))
  28. buf = append(w.prefix[:], buf...)
  29. n, err = w.Writer.Write(buf)
  30. return n - StdWriterPrefixLen, err
  31. }
  32. // NewStdWriter instanciate a new Writer based on the given type `t`.
  33. // the utils package contains the valid parametres for `t`:
  34. func NewStdWriter(w io.Writer, t StdType) *StdWriter {
  35. if len(t) != StdWriterPrefixLen {
  36. return nil
  37. }
  38. return &StdWriter{
  39. Writer: w,
  40. prefix: t,
  41. sizeBuf: make([]byte, 4),
  42. }
  43. }
  44. var ErrInvalidStdHeader = errors.New("Unrecognized input header")
  45. // StdCopy is a modified version of io.Copy.
  46. //
  47. // StdCopy copies from src to dstout or dsterr until either EOF is reached
  48. // on src or an error occurs. It returns the number of bytes
  49. // copied and the first error encountered while copying, if any.
  50. //
  51. // A successful Copy returns err == nil, not err == EOF.
  52. // Because Copy is defined to read from src until EOF, it does
  53. // not treat an EOF from Read as an error to be reported.
  54. //
  55. // The source needs to be writter via StdWriter, dstout or dsterr is selected
  56. // based on the prefix added by StdWriter
  57. func StdCopy(dstout, dsterr io.Writer, src io.Reader) (written int64, err error) {
  58. var (
  59. buf = make([]byte, 32*1024+StdWriterPrefixLen+1)
  60. bufLen = len(buf)
  61. nr, nw int
  62. er, ew error
  63. out io.Writer
  64. frameSize int
  65. )
  66. for {
  67. // Make sure we have at least a full header
  68. for nr < StdWriterPrefixLen {
  69. var nr2 int
  70. nr2, er = src.Read(buf[nr:])
  71. if er == io.EOF {
  72. return written, nil
  73. }
  74. if er != nil {
  75. return 0, er
  76. }
  77. nr += nr2
  78. }
  79. // Check the first byte to know where to write
  80. switch buf[StdWriterFdIndex] {
  81. case 0:
  82. fallthrough
  83. case 1:
  84. // Write on stdout
  85. out = dstout
  86. case 2:
  87. // Write on stderr
  88. out = dsterr
  89. default:
  90. Debugf("Error selecting output fd: (%d)", buf[StdWriterFdIndex])
  91. return 0, ErrInvalidStdHeader
  92. }
  93. // Retrieve the size of the frame
  94. frameSize = int(binary.BigEndian.Uint32(buf[StdWriterSizeIndex : StdWriterSizeIndex+4]))
  95. // Check if the buffer is big enough to read the frame.
  96. // Extend it if necessary.
  97. if frameSize+StdWriterPrefixLen > bufLen {
  98. Debugf("Extending buffer cap.")
  99. buf = append(buf, make([]byte, frameSize-len(buf)+1)...)
  100. bufLen = len(buf)
  101. }
  102. // While the amount of bytes read is less than the size of the frame + header, we keep reading
  103. for nr < frameSize+StdWriterPrefixLen {
  104. var nr2 int
  105. nr2, er = src.Read(buf[nr:])
  106. if er == io.EOF {
  107. return written, nil
  108. }
  109. if er != nil {
  110. Debugf("Error reading frame: %s", er)
  111. return 0, er
  112. }
  113. nr += nr2
  114. }
  115. // Write the retrieved frame (without header)
  116. nw, ew = out.Write(buf[StdWriterPrefixLen : frameSize+StdWriterPrefixLen])
  117. if nw > 0 {
  118. written += int64(nw)
  119. }
  120. if ew != nil {
  121. Debugf("Error writing frame: %s", ew)
  122. return 0, ew
  123. }
  124. // If the frame has not been fully written: error
  125. if nw != frameSize {
  126. Debugf("Error Short Write: (%d on %d)", nw, frameSize)
  127. return 0, io.ErrShortWrite
  128. }
  129. // Move the rest of the buffer to the beginning
  130. copy(buf, buf[frameSize+StdWriterPrefixLen:])
  131. // Move the index
  132. nr -= frameSize + StdWriterPrefixLen
  133. }
  134. }