/decoders/binary/yEnc.d

http://github.com/wilkie/djehuty · D · 325 lines · 197 code · 85 blank · 43 comment · 80 complexity · 91baa1e0a4ad4751187e12a9bc1de4af MD5 · raw file

  1. /*
  2. * yEnc.d
  3. *
  4. * This file implements the yEnc algorithm.
  5. *
  6. * Author: Dave Wilkinson
  7. *
  8. */
  9. module decoders.binary.yEnc;
  10. import core.endian;
  11. import core.stream;
  12. import core.definitions;
  13. import decoders.binary.decoder;
  14. private {
  15. const auto YENC_STATE_INIT = 0;
  16. const auto YENC_STATE_READHEADER = 1;
  17. const auto YENC_STATE_READHEADERVALUE = 2;
  18. const auto YENC_STATE_READHEADERNAME = 3;
  19. const auto YENC_STATE_READLINE_START = 4;
  20. const auto YENC_STATE_READLINE = 5;
  21. const auto YENC_STATE_READ_ESCAPE = 6;
  22. const auto YENC_STATE_READFOOTER = 7;
  23. }
  24. // Section: Codecs/Binary
  25. // Description: This represents the yEnc Codec.
  26. class yEncDecoder : BinaryDecoder {
  27. StreamData decode(Stream stream, Stream toStream) {
  28. ushort chunk;
  29. char linestr[257];
  30. uint line;
  31. uint size;
  32. ubyte chr;
  33. ubyte linepos = 0;
  34. ubyte headeroption = 0;
  35. for (;;) {
  36. switch (decoderState) {
  37. case YENC_STATE_INIT:
  38. decoderState = YENC_STATE_READHEADER;
  39. if(!stream.read(chunk)) {
  40. return StreamData.Required;
  41. }
  42. if (chunk == ('=' | ('y'<<8))) {
  43. //escape sequence (valid header)
  44. linepos = 0;
  45. decoderState = YENC_STATE_READHEADER;
  46. }
  47. else {
  48. return StreamData.Invalid;
  49. }
  50. continue;
  51. case YENC_STATE_READHEADER:
  52. // read the rest of the line
  53. if(!stream.read(chr)) {
  54. return StreamData.Required;
  55. }
  56. if ((chr == ' ') || (chr == '=')) {
  57. // delimiter
  58. linestr[linepos] = 0;
  59. linepos = 0;
  60. // get the token
  61. if (!(linestr == "line")) {
  62. decoderState = YENC_STATE_READHEADERVALUE;
  63. headeroption = 0;
  64. continue;
  65. }
  66. else if (!(linestr == "size")) {
  67. decoderState = YENC_STATE_READHEADERVALUE;
  68. headeroption = 1;
  69. continue;
  70. }
  71. else if (!(linestr == "name")) {
  72. decoderState = YENC_STATE_READHEADERNAME;
  73. headeroption = 2;
  74. continue;
  75. }
  76. }
  77. else if (chr == '\r') {
  78. continue;
  79. }
  80. else if (chr == '\n') {
  81. decoderState = YENC_STATE_READLINE_START;
  82. continue;
  83. }
  84. else {
  85. linestr[linepos] = chr;
  86. linepos++;
  87. }
  88. continue;
  89. case YENC_STATE_READHEADERVALUE:
  90. // ignore whitespace, =, etc until a value is read
  91. // read the rest of the line
  92. if(!stream.read(chr)) {
  93. return StreamData.Required;
  94. }
  95. if ((chr == ' ') || (chr == '=') || (chr == '\n') || (chr == '\r')) {
  96. if (linepos == 0) {
  97. // ignore this
  98. continue;
  99. }
  100. // delimiter
  101. linestr[linepos] = 0;
  102. // get the token
  103. if (headeroption == 0) {
  104. // TODO: Int to String Functions
  105. //line = cast(uint)atoi(cast(StringLiteral8)linestr);
  106. } else {
  107. // TODO: Int to String Functions
  108. //size = cast(uint)atoi(cast(StringLiteral8)linestr);
  109. }
  110. if (chr == '\r') {
  111. continue;
  112. }
  113. else if (chr == '\n') {
  114. decoderState = YENC_STATE_READLINE_START;
  115. continue;
  116. }
  117. else {
  118. linepos = 0;
  119. decoderState = YENC_STATE_READHEADER;
  120. }
  121. continue;
  122. }
  123. linestr[linepos] = chr;
  124. linepos++;
  125. continue;
  126. case YENC_STATE_READHEADERNAME:
  127. // ignore whitespace, =, etc until a value is read
  128. // read the rest of the line
  129. if(!stream.read(chr)) {
  130. return StreamData.Required;
  131. }
  132. if ((chr == ' ') || (chr == '=') || (chr == '\n') || (chr == '\r')) {
  133. if (linepos == 0) {
  134. // ignore this
  135. continue;
  136. }
  137. // delimiter
  138. linestr[linepos] = 0;
  139. // get the token
  140. //if (headeroption == 0)
  141. //{
  142. // line = atoi(linestr);
  143. //} else {
  144. // size = atoi(linestr);
  145. //}
  146. if (chr == '\r') {
  147. continue;
  148. }
  149. else if (chr == '\n') {
  150. decoderState = YENC_STATE_READLINE_START;
  151. continue;
  152. }
  153. else {
  154. linepos = 0;
  155. decoderState = YENC_STATE_READHEADER;
  156. }
  157. continue;
  158. }
  159. linestr[linepos] = chr;
  160. linepos++;
  161. continue;
  162. case YENC_STATE_READLINE_START:
  163. if(!stream.read(chunk)) {
  164. return StreamData.Required;
  165. }
  166. if (chunk == ('=' | ('y'<<8))) {
  167. //escape sequence (valid footer)
  168. linepos = 0;
  169. decoderState = YENC_STATE_READFOOTER;
  170. continue;
  171. }
  172. else {
  173. // decode these two bytes
  174. // DECODE!
  175. chr = cast(ubyte)(chunk & 0xFF);
  176. if (chr == '=') {
  177. chr = cast(ubyte)(chunk >> 8);
  178. chr -= 64;
  179. toStream.write(&chr, 1);
  180. }
  181. else {
  182. chr -= 42;
  183. toStream.write(&chr, 1);
  184. chr = cast(ubyte)(chunk >> 8);
  185. if (chr == '=')
  186. {
  187. decoderState = YENC_STATE_READ_ESCAPE;
  188. continue;
  189. }
  190. else
  191. {
  192. chr -= 42;
  193. toStream.write(&chr, 1);
  194. }
  195. }
  196. decoderState = YENC_STATE_READLINE;
  197. }
  198. continue;
  199. case YENC_STATE_READLINE:
  200. // pull a byte, decode it!
  201. if(!stream.read(chr)) {
  202. return StreamData.Required;
  203. }
  204. if (chr == '\r') {
  205. // ignore carriage returns
  206. continue;
  207. }
  208. if (chr == '\n') {
  209. // line feeds... goto next line!
  210. // check for accuracy?
  211. decoderState = YENC_STATE_READLINE_START;
  212. continue;
  213. }
  214. if (chr == '=') {
  215. decoderState = YENC_STATE_READ_ESCAPE;
  216. continue;
  217. }
  218. // decode character
  219. chr -= 42;
  220. toStream.write(&chr, 1);
  221. continue;
  222. case YENC_STATE_READ_ESCAPE:
  223. if(!stream.read(chr)) {
  224. return StreamData.Required;
  225. }
  226. // decode character
  227. chr -= 106;
  228. toStream.write(&chr, 1);
  229. decoderState = YENC_STATE_READLINE;
  230. continue;
  231. case YENC_STATE_READFOOTER:
  232. return StreamData.Complete;
  233. default:
  234. break;
  235. }
  236. break;
  237. }
  238. return StreamData.Invalid;
  239. }
  240. }