import { assert , unreachableCase } from "@fluidframework/common-utils" ; import { ReadBuffer } from "./ReadBufferUtils" ; import { BlobCore , codeToBytesMap , getValueSafely , MarkerCodes , MarkerCodesEnd , MarkerCodesStart , NodeCore , TreeBuilder , } from "./zipItDataRepresentationUtils" ; /**
 * Buffer class, used to sequentially writ data.
 * Used by tree code to serialize tree into binary representation.
 */ export class WriteBuffer { protected data ? : Uint8Array = new Uint8Array ( 4096 ) ; protected index = 0 ; protected push ( code ) { assert ( this . data !== undefined , 0x225 ) ; const length = this . data . length ; if ( this . index === length ) { const newData = new Uint8Array ( length * 1.2 + 4096 ) ; let index = 0 ; const oldData = this . data ; while ( index < length ) { newData [ index ] = oldData [ index ] ; index ++ ; } this . data = newData ; } this . data [ this . index ] = code % 256 ; this . index ++ ; } public write ( codeArg , lengthArg = 1 ) { let code = codeArg ; let length = lengthArg ; while ( length > 0 ) { this . push ( code % 256 ) ; code = Math . floor ( code / 256 ) ; length -- ; } assert ( code === 0 , 0x226 ) ; } public done ( ) { assert ( this . data !== undefined , 0x227 ) ; const buffer = new ReadBuffer ( this . data . subarray ( 0 , this . index ) ) ; this . data = undefined ; return buffer ; } } /**
 * This contains mapping of number of bytes representing the corresponding string length to Marker Codes.
*/ const utf8StringBytesToCodeMap = { 0 : 13 , 1 : 14 , 2 : 15 , 4 : 16 } ; /**
 * This contains mapping of number of bytes representing the corresponding length in which actual data(base64 string)
 * will be stored to Marker Codes.
*/ const binaryBytesToCodeMap = { 0 : 32 , 1 : 33 , 2 : 34 , 4 : 35 , 8 : 16 } ; /**
 * This contains mapping of number of bytes representing the corresponding const string id to Marker Codes.
*/ const constStringBytesToCodeMap = { 1 : 17 , 2 : 18 , 4 : 19 } ; /**
 * This contains mapping of number of bytes to Marker Codes representing the corresponding Integer.
*/ const integerBytesToCodeMap = { 0 : 1 , 1 : 3 , 2 : 5 , 4 : 7 , 8 : 9 } ; /**
 * This contains mapping of boolean to Marker Codes representing the corresponding bool value.
*/ const boolToCodeMap = [ 12 , 11 , ] ; param num export function calcLength ( numArg ) { if ( numArg === 0 ) { return 0 ; } let num = numArg ; let lengthLen = 0 ; while ( num > 0 ) { num = Math . floor ( num / 256 ) ; lengthLen ++ ; } let res = 1 ; while ( res < lengthLen ) { res *= 2 ; } return res ; } param buffer param blob param dictionary function serializeBlob ( buffer , blob , dictionary ) { const data = blob . buffer ; const lengthOfDataLen = calcLength ( data . length ) ; if ( blob . constString ) { const content = blob . toString ( ) ; let id = dictionary . get ( content ) ; let idLength ; if ( id === undefined ) { id = dictionary . size + 1 ; idLength = calcLength ( id ) ; dictionary . set ( content , id ) ; let code ; if ( lengthOfDataLen > 1 || idLength > 1 ) { code = MarkerCodes . ConstStringDeclareBig ; } else { code = MarkerCodes . ConstStringDeclare ; } buffer . write ( code ) ; const bytes = getValueSafely ( codeToBytesMap , code ) ; assert ( bytes >= lengthOfDataLen , 0x283 ) ; assert ( bytes >= idLength , 0x284 ) ; buffer . write ( id , bytes ) ; buffer . write ( data . length , bytes ) ; for ( const element of data ) { buffer . write ( element ) ; } } else { idLength = calcLength ( id ) ; } buffer . write ( getValueSafely ( constStringBytesToCodeMap , idLength ) ) ; buffer . write ( id , idLength ) ; } else { buffer . write ( blob . useUtf8Code ? getValueSafely ( utf8StringBytesToCodeMap , lengthOfDataLen ) : getValueSafely ( binaryBytesToCodeMap , lengthOfDataLen ) ) ; if ( lengthOfDataLen > 0 ) { buffer . write ( data . length , lengthOfDataLen ) ; for ( const element of data ) { buffer . write ( element ) ; } } } } param buffer param nodeCore param dictionary function serializeNodeCore ( buffer , nodeCore , dictionary ) { for ( const child of nodeCore . nodes ) { if ( child instanceof NodeCore ) { const startCode = MarkerCodesStart [ child . type ] ; const endCode = MarkerCodesEnd [ child . type ] ; assert ( startCode !== undefined , 0x285 ) ; assert ( endCode !== undefined , 0x286 ) ; buffer . write ( startCode ) ; serializeNodeCore ( buffer , child , dictionary ) ; buffer . write ( endCode ) ; } else if ( child instanceof BlobCore ) { serializeBlob ( buffer , child , dictionary ) ; } else if ( typeof child === "number" ) { const len = calcLength ( child ) ; buffer . write ( getValueSafely ( integerBytesToCodeMap , len ) ) ; if ( len > 0 ) { buffer . write ( child , len ) ; } } else if ( typeof child === "boolean" ) { buffer . write ( boolToCodeMap [ child ? 1 : 0 ] ) ; } else { unreachableCase ( child , "Unsupported node type" ) ; } } } class NodeCoreSerializer extends NodeCore { constructor ( ) { super ( ) ; } public serialize ( buffer ) { serializeNodeCore ( buffer , this , new Map < string , number > ( ) ) ; } } export class TreeBuilderSerializer extends NodeCoreSerializer { constructor ( ) { super ( ) ; } static load ( buffer ) { return TreeBuilder . load ( buffer ) ; } public serialize ( ) { const buffer = new WriteBuffer ( ) ; super . serialize ( buffer ) ; return buffer . done ( ) ; } }