writer.go 2.6 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132
  1. package compressor
  2. import (
  3. "bytes"
  4. "io"
  5. "unicode/utf8"
  6. "github.com/muesli/ansi"
  7. )
  8. type Writer struct {
  9. Forward io.Writer
  10. ansi bool
  11. ansiseq bytes.Buffer
  12. lastseq bytes.Buffer
  13. prevlastseq bytes.Buffer
  14. resetreq bool
  15. runeBuf []byte
  16. compressed int
  17. uncompressed int
  18. }
  19. // Bytes is shorthand for declaring a new default compressor instance,
  20. // used to immediately compress a byte slice.
  21. func Bytes(b []byte) []byte {
  22. var buf bytes.Buffer
  23. f := Writer{
  24. Forward: &buf,
  25. }
  26. _, _ = f.Write(b)
  27. _ = f.Close()
  28. return buf.Bytes()
  29. }
  30. // String is shorthand for declaring a new default compressor instance,
  31. // used to immediately compress a string.
  32. func String(s string) string {
  33. return string(Bytes([]byte(s)))
  34. }
  35. // Write is used to write content to the ANSI buffer.
  36. func (w *Writer) Write(b []byte) (int, error) {
  37. w.uncompressed += len(b)
  38. for _, c := range string(b) {
  39. if c == ansi.Marker {
  40. // ANSI escape sequence
  41. w.ansi = true
  42. _, _ = w.ansiseq.WriteRune(c)
  43. } else if w.ansi {
  44. _, _ = w.ansiseq.WriteRune(c)
  45. if ansi.IsTerminator(c) {
  46. // ANSI sequence terminated
  47. w.ansi = false
  48. terminated := false
  49. if bytes.HasSuffix(w.ansiseq.Bytes(), []byte("[0m")) {
  50. // reset sequence
  51. w.prevlastseq.Reset()
  52. w.prevlastseq.Write(w.lastseq.Bytes())
  53. w.lastseq.Reset()
  54. terminated = true
  55. w.resetreq = true
  56. } else if c == 'm' {
  57. // color code
  58. _, _ = w.lastseq.Write(w.ansiseq.Bytes())
  59. }
  60. if !terminated {
  61. // did we reset the sequence just to restore it again?
  62. if bytes.Equal(w.ansiseq.Bytes(), w.prevlastseq.Bytes()) {
  63. w.resetreq = false
  64. w.ansiseq.Reset()
  65. }
  66. w.prevlastseq.Reset()
  67. if w.resetreq {
  68. w.ResetAnsi()
  69. }
  70. _, _ = w.Forward.Write(w.ansiseq.Bytes())
  71. w.compressed += w.ansiseq.Len()
  72. }
  73. w.ansiseq.Reset()
  74. }
  75. } else {
  76. if w.resetreq {
  77. w.ResetAnsi()
  78. }
  79. _, err := w.writeRune(c)
  80. if err != nil {
  81. return 0, err
  82. }
  83. }
  84. }
  85. return len(b), nil
  86. }
  87. func (w *Writer) writeRune(r rune) (int, error) {
  88. if w.runeBuf == nil {
  89. w.runeBuf = make([]byte, utf8.UTFMax)
  90. }
  91. n := utf8.EncodeRune(w.runeBuf, r)
  92. w.compressed += n
  93. return w.Forward.Write(w.runeBuf[:n])
  94. }
  95. // Close finishes the compression operation. Always call it before trying to
  96. // retrieve the final result.
  97. func (w *Writer) Close() error {
  98. if w.resetreq {
  99. w.ResetAnsi()
  100. }
  101. // log.Println("Written uncompressed: ", w.uncompressed)
  102. // log.Println("Written compressed: ", w.compressed)
  103. return nil
  104. }
  105. func (w *Writer) ResetAnsi() {
  106. w.prevlastseq.Reset()
  107. _, _ = w.Forward.Write([]byte("\x1b[0m"))
  108. w.resetreq = false
  109. }