No Description

encoder.go 5.1KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194
  1. // Copyright (C) 2017 Keelan Lightfoot
  2. // Copyright (C) 2007-2008 Board of Regents of the University of Wisconsin
  3. // System (Univ. of Wisconsin-Madison, Trace R&D Center)
  4. // Copyright (C) 2007-2008 Omnitor AB
  5. // Copyright (C) 2007-2008 Voiceriver Inc
  6. //
  7. // This library is free software; you can redistribute it and/or modify it
  8. // under the terms of the GNU Lesser General Public License as published by
  9. // the Free Software Foundation; either version 2.1 of the License, or (at
  10. // your option) any later version.
  11. package gortty
  12. import (
  13. "bytes"
  14. "context"
  15. "fmt"
  16. )
  17. // Encoder is fun
  18. type Encoder struct {
  19. input chan rune
  20. output chan byte
  21. autoWrap bool // true for auto CRLF mode
  22. autoCRLF bool
  23. charset *CodeSet
  24. crlfThresh int // start looking for a space to replace with a crlf at this threshold
  25. crlfForceThresh int // force a crlf if it hasn't happend by this threshold
  26. shiftThresh int
  27. callback Callback
  28. }
  29. // NewEncoder is fun
  30. func NewEncoder(ctx context.Context, input chan rune, output chan byte, charset *CodeSet) *Encoder {
  31. e := new(Encoder)
  32. if input == nil {
  33. e.input = make(chan rune)
  34. } else {
  35. e.input = input
  36. }
  37. e.output = output
  38. e.charset = charset
  39. e.crlfThresh = 60
  40. e.crlfForceThresh = 72
  41. e.shiftThresh = 70
  42. go e.run(ctx)
  43. return e
  44. }
  45. // EncodeString is fun
  46. func (e *Encoder) EncodeString(s string) {
  47. for _, c := range s {
  48. e.input <- c
  49. }
  50. }
  51. // SetAutoCRLF is fun
  52. func (e *Encoder) SetAutoCRLF(autoCRLF bool) {
  53. e.autoCRLF = autoCRLF
  54. }
  55. // SetCallback is fun
  56. func (e *Encoder) SetCallback(callback Callback) {
  57. e.callback = callback
  58. }
  59. func (e *Encoder) run(ctx context.Context) {
  60. var modCase int // current Letters/Figures state
  61. var charsWithoutCRLF int // number of chars we have handled without inserting a CR-LF
  62. var charsWithoutShift int // number of chars we have handled without seeing a shift
  63. for {
  64. select {
  65. case <-ctx.Done():
  66. return
  67. case r := <-e.input:
  68. c := e.charset.toCode(r)
  69. /*this rune can either be rendered as a Figures character,
  70. a Letters character or WHITESPACE which can be rendered by
  71. either character set*/
  72. if r == '\r' || r == '\n' {
  73. charsWithoutCRLF = 0
  74. }
  75. /*see if we need to first insert a case character*/
  76. if c.shift == shiftWhitespace {
  77. /*we have white space, we don't need to insert
  78. a Letters/Figures case character. We will insert
  79. a case character when we transition from white
  80. space to some other case. The only exception to this
  81. is when we are just starting to transmit, in which case
  82. we must send something, so we'll choose the more common
  83. Letters case*/
  84. if modCase == shiftUnknown {
  85. e.output <- e.charset.unshift()
  86. }
  87. modCase = shiftWhitespace
  88. } else if c.shift != modCase {
  89. /*case is either Letters or Figures and doesn't match
  90. our modulator's current state, so add a new case char
  91. here*/
  92. if c.shift == shiftLetter {
  93. e.output <- e.charset.unshift()
  94. } else {
  95. e.output <- e.charset.shift()
  96. }
  97. /*and save our current state*/
  98. modCase = c.shift
  99. /*and update our counter*/
  100. //fmt.Println("charsWithoutShift", charsWithoutShift)
  101. charsWithoutShift = 0
  102. }
  103. /*we need to perform a little logic here to check for some
  104. * boundry conditions in the protocol*/
  105. /* We need to ensure that we re-send the current case character
  106. at least every 72 characters. */
  107. if charsWithoutShift > e.shiftThresh {
  108. if modCase == shiftWhitespace {
  109. e.output <- e.charset.unshift() /*doesn't matter which, so how 'bout LETR*/
  110. } else if modCase == shiftLetter {
  111. e.output <- e.charset.unshift()
  112. } else {
  113. e.output <- e.charset.shift()
  114. }
  115. //fmt.Println("charsWithoutShift", charsWithoutShift)
  116. charsWithoutShift = 0
  117. }
  118. /* we need to ensure that we send a carriage return/linefeed
  119. * at least every 72 characters, but we start looking for a
  120. * space character starting at 60 characters */
  121. if !e.autoWrap {
  122. /*we just insert our regular character*/
  123. e.output <- c.toByte()
  124. if e.autoCRLF {
  125. if r == '\n' {
  126. e.output <- e.charset.toByte('\r')
  127. } else if r == '\r' {
  128. e.output <- e.charset.toByte('\n')
  129. }
  130. }
  131. } else if (charsWithoutCRLF > e.crlfThresh) && (modCase == shiftWhitespace) {
  132. /*insert the cr-lf instead of the space character*/
  133. e.output <- e.charset.toByte('\r')
  134. e.output <- e.charset.toByte('\n')
  135. charsWithoutCRLF = 0
  136. } else if charsWithoutCRLF > e.crlfForceThresh {
  137. /*insert the cr-lf followed by the character*/
  138. e.output <- e.charset.toByte('\r')
  139. e.output <- e.charset.toByte('\n')
  140. e.output <- c.toByte()
  141. charsWithoutCRLF = 0
  142. } else {
  143. /*we just insert our regular character*/
  144. e.output <- c.toByte()
  145. if e.autoCRLF {
  146. if r == '\n' {
  147. e.output <- e.charset.toByte('\r')
  148. } else if r == '\r' {
  149. e.output <- e.charset.toByte('\n')
  150. }
  151. }
  152. }
  153. charsWithoutShift++
  154. charsWithoutCRLF++
  155. }
  156. }
  157. }
  158. func (e *Encoder) Write(p []byte) (n int, err error) {
  159. runes := bytes.Runes(p)
  160. fmt.Print(string(runes))
  161. for _, r := range runes {
  162. e.input <- r
  163. }
  164. return len(p), nil
  165. }