Not a member of Pastebin yet?
Sign Up,
it unlocks many cool features!
- "use strict";
- //has to be replaced, when used in FE
- const EventEmitter = require("events");
- const HIGH_DELIMITER = "|";
- const LOW_DELIMITER = ",";
- const MAGIC_DELIMITER = "?";
- class Fragment {
- 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>"){
- this.id = id;
- this.scripts = scripts;
- this.styles = styles;
- if(!Buffer.isBuffer(content)){
- content = new Buffer(content);
- }
- this.content = content;
- }
- }
- class Serializer {
- static getFragmentHeaders(fragment){
- return {
- id: fragment.id.length,
- scripts: fragment.scripts.map(script => script.length),
- styles: fragment.styles.map(style => style.length),
- content: fragment.content.length
- };
- }
- static serializeToStream(fragments, stream){
- fragments = Array.isArray(fragments) ? fragments : [fragments];
- fragments.forEach(fragment => {
- //build fragment header
- const header = [];
- const headers = Serializer.getFragmentHeaders(fragment);
- header.push(headers.id);
- header.push(HIGH_DELIMITER);
- headers.scripts.forEach(script => {
- header.push(script);
- header.push(LOW_DELIMITER);
- });
- header.push(HIGH_DELIMITER);
- headers.styles.forEach(style => {
- header.push(style);
- header.push(LOW_DELIMITER);
- });
- header.push(HIGH_DELIMITER);
- header.push(headers.content);
- //calculate size of header and prepend before head with magic delim
- let headerSize = 0;
- header.forEach(segment => {
- if(typeof segment !== "string"){
- headerSize += (segment + "").length;
- } else {
- headerSize += segment.length;
- }
- });
- //+ 1 because of additional delimiter
- header.unshift(headerSize + 1, MAGIC_DELIMITER);
- //build fragment body
- const body = [];
- body.push(fragment.id);
- fragment.scripts.forEach(script => body.push(script));
- fragment.styles.forEach(style => body.push(style));
- body.push(fragment.content);
- //write header and body to stream
- header.forEach(chunk => stream.write(chunk));
- stream.write(HIGH_DELIMITER);
- body.forEach(chunk => stream.write(chunk));
- });
- }
- }
- class Deserializer extends EventEmitter {
- constructor(){
- super();
- }
- static parseHeader(headerString){
- const header = {
- id: -1,
- scripts: [],
- styles: [],
- content: -1,
- };
- try {
- const split = headerString.split(HIGH_DELIMITER).filter(segment => !!segment);
- header.id = parseInt(split[0]);
- header.scripts = split[1].split(LOW_DELIMITER).filter(segment => !!segment).map(segment => parseInt(segment));
- header.styles = split[2].split(LOW_DELIMITER).filter(segment => !!segment).map(segment => parseInt(segment));
- header.content = parseInt(split[3]);
- } catch(error){
- console.error(error);
- }
- return header;
- }
- streamAsFragments(chunk){
- //TODO this can only stream full fragment strings for now
- let found = false;
- let magicIndex = 0;
- for(let c of chunk){
- if(c === MAGIC_DELIMITER){
- found = true;
- break;//end here
- }
- magicIndex++;
- }
- if(!found){
- return console.log("no magic delimiter found.");
- }
- const headSize = parseInt(chunk.substring(0, magicIndex));
- const contentStart = magicIndex + 1 + headSize;
- const headerString = chunk.substring(magicIndex + 1, contentStart);
- const header = Deserializer.parseHeader(headerString);
- const id = chunk.substring(contentStart, contentStart + header.id);
- let chunkIndex = contentStart + header.id;
- const scripts = header.scripts.map(scriptLen => {
- const script = chunk.substring(chunkIndex, chunkIndex + scriptLen);
- chunkIndex += scriptLen;
- return script;
- });
- const styles = header.styles.map(styleLen => {
- const style = chunk.substring(chunkIndex, chunkIndex + styleLen);
- chunkIndex += styleLen;
- return style;
- });
- const content = chunk.substring(chunkIndex, chunkIndex + header.content);
- chunkIndex += header.content;
- super.emit("fragment", new Fragment(id, scripts, styles, content));
- //check if the chunk contains more than a single fragment
- if(chunkIndex < chunk.length - 1){
- this.streamAsFragments(chunk.substring(chunkIndex, chunk.length - 1));
- }
- }
- }
- //helper class to debug
- class FakeStream extends EventEmitter {
- constructor(){
- super();
- this.data = [];
- }
- write(chunk){
- this.data.push(chunk);
- }
- end(){
- super.emit("end", this.data.join(""));
- this.data = [];
- }
- }
- //example
- const frags = [
- new Fragment(),
- new Fragment(),
- new Fragment()
- ];
- console.log(frags);
- const stream = new FakeStream();
- stream.on("end", res => {
- console.log(res);
- const deserializer = new Deserializer();
- deserializer.on("fragment", frag => {
- console.log(frag);
- });
- deserializer.streamAsFragments(res);
- });
- Serializer.serializeToStream(frags, stream);
- stream.end();
Add Comment
Please, Sign In to add comment