Guest User

Untitled

a guest
Nov 25th, 2017
92
0
Never
Not a member of Pastebin yet? Sign Up, it unlocks many cool features!
text 5.68 KB | None | 0 0
  1. "use strict";
  2.  
  3. //has to be replaced, when used in FE
  4. const EventEmitter = require("events");
  5.  
  6. const HIGH_DELIMITER = "|";
  7. const LOW_DELIMITER = ",";
  8. const MAGIC_DELIMITER = "?";
  9.  
  10. class Fragment {
  11.  
  12. constructor(id = "1-2-3-4-5-6", scripts = ["my.js?one=two"], styles = ["my.css"], content = "<a href='/123'><h1>|bla,bla|</h1></a>"){
  13. this.id = id;
  14. this.scripts = scripts;
  15. this.styles = styles;
  16.  
  17. if(!Buffer.isBuffer(content)){
  18. content = new Buffer(content);
  19. }
  20.  
  21. this.content = content;
  22. }
  23. }
  24.  
  25. class Serializer {
  26.  
  27. static getFragmentHeaders(fragment){
  28. return {
  29. id: fragment.id.length,
  30. scripts: fragment.scripts.map(script => script.length),
  31. styles: fragment.styles.map(style => style.length),
  32. content: fragment.content.length
  33. };
  34. }
  35.  
  36. static serializeToStream(fragments, stream){
  37. fragments = Array.isArray(fragments) ? fragments : [fragments];
  38. fragments.forEach(fragment => {
  39.  
  40. //build fragment header
  41. const header = [];
  42. const headers = Serializer.getFragmentHeaders(fragment);
  43.  
  44. header.push(headers.id);
  45. header.push(HIGH_DELIMITER);
  46.  
  47. headers.scripts.forEach(script => {
  48. header.push(script);
  49. header.push(LOW_DELIMITER);
  50. });
  51. header.push(HIGH_DELIMITER);
  52.  
  53. headers.styles.forEach(style => {
  54. header.push(style);
  55. header.push(LOW_DELIMITER);
  56. });
  57. header.push(HIGH_DELIMITER);
  58.  
  59. header.push(headers.content);
  60.  
  61. //calculate size of header and prepend before head with magic delim
  62. let headerSize = 0;
  63. header.forEach(segment => {
  64. if(typeof segment !== "string"){
  65. headerSize += (segment + "").length;
  66. } else {
  67. headerSize += segment.length;
  68. }
  69. });
  70.  
  71. //+ 1 because of additional delimiter
  72. header.unshift(headerSize + 1, MAGIC_DELIMITER);
  73.  
  74. //build fragment body
  75. const body = [];
  76.  
  77. body.push(fragment.id);
  78. fragment.scripts.forEach(script => body.push(script));
  79. fragment.styles.forEach(style => body.push(style));
  80. body.push(fragment.content);
  81.  
  82. //write header and body to stream
  83. header.forEach(chunk => stream.write(chunk));
  84. stream.write(HIGH_DELIMITER);
  85. body.forEach(chunk => stream.write(chunk));
  86. });
  87. }
  88. }
  89.  
  90. class Deserializer extends EventEmitter {
  91.  
  92. constructor(){
  93. super();
  94. }
  95.  
  96. static parseHeader(headerString){
  97.  
  98. const header = {
  99. id: -1,
  100. scripts: [],
  101. styles: [],
  102. content: -1,
  103. };
  104.  
  105. try {
  106. const split = headerString.split(HIGH_DELIMITER).filter(segment => !!segment);
  107. header.id = parseInt(split[0]);
  108. header.scripts = split[1].split(LOW_DELIMITER).filter(segment => !!segment).map(segment => parseInt(segment));
  109. header.styles = split[2].split(LOW_DELIMITER).filter(segment => !!segment).map(segment => parseInt(segment));
  110. header.content = parseInt(split[3]);
  111. } catch(error){
  112. console.error(error);
  113. }
  114.  
  115. return header;
  116. }
  117.  
  118. streamAsFragments(chunk){
  119.  
  120. //TODO this can only stream full fragment strings for now
  121.  
  122. let found = false;
  123. let magicIndex = 0;
  124. for(let c of chunk){
  125. if(c === MAGIC_DELIMITER){
  126. found = true;
  127. break;//end here
  128. }
  129. magicIndex++;
  130. }
  131.  
  132. if(!found){
  133. return console.log("no magic delimiter found.");
  134. }
  135.  
  136. const headSize = parseInt(chunk.substring(0, magicIndex));
  137. const contentStart = magicIndex + 1 + headSize;
  138. const headerString = chunk.substring(magicIndex + 1, contentStart);
  139. const header = Deserializer.parseHeader(headerString);
  140.  
  141. const id = chunk.substring(contentStart, contentStart + header.id);
  142.  
  143. let chunkIndex = contentStart + header.id;
  144. const scripts = header.scripts.map(scriptLen => {
  145. const script = chunk.substring(chunkIndex, chunkIndex + scriptLen);
  146. chunkIndex += scriptLen;
  147. return script;
  148. });
  149.  
  150. const styles = header.styles.map(styleLen => {
  151. const style = chunk.substring(chunkIndex, chunkIndex + styleLen);
  152. chunkIndex += styleLen;
  153. return style;
  154. });
  155.  
  156. const content = chunk.substring(chunkIndex, chunkIndex + header.content);
  157. chunkIndex += header.content;
  158.  
  159. super.emit("fragment", new Fragment(id, scripts, styles, content));
  160.  
  161. //check if the chunk contains more than a single fragment
  162. if(chunkIndex < chunk.length - 1){
  163. this.streamAsFragments(chunk.substring(chunkIndex, chunk.length - 1));
  164. }
  165. }
  166. }
  167.  
  168. //helper class to debug
  169. class FakeStream extends EventEmitter {
  170.  
  171. constructor(){
  172. super();
  173. this.data = [];
  174. }
  175.  
  176. write(chunk){
  177. this.data.push(chunk);
  178. }
  179.  
  180. end(){
  181. super.emit("end", this.data.join(""));
  182. this.data = [];
  183. }
  184. }
  185.  
  186. //example
  187.  
  188. const frags = [
  189. new Fragment(),
  190. new Fragment(),
  191. new Fragment()
  192. ];
  193.  
  194. console.log(frags);
  195.  
  196. const stream = new FakeStream();
  197.  
  198. stream.on("end", res => {
  199. console.log(res);
  200.  
  201. const deserializer = new Deserializer();
  202. deserializer.on("fragment", frag => {
  203. console.log(frag);
  204. });
  205. deserializer.streamAsFragments(res);
  206. });
  207.  
  208. Serializer.serializeToStream(frags, stream);
  209. stream.end();
Add Comment
Please, Sign In to add comment