import { assert } from "@fluidframework/common-utils" ; import { IGarbageCollectionData , IGarbageCollectionNodeData , IGarbageCollectionState , IGarbageCollectionDetailsBase , } from "@fluidframework/runtime-definitions" ; param gcData returns export function cloneGCData ( gcData ) { const clonedGCNodes = { } ; for ( const [ id , outboundRoutes ] of Object . entries ( gcData . gcNodes ) ) { clonedGCNodes [ id ] = Array . from ( outboundRoutes ) ; } return { gcNodes : clonedGCNodes } ; } param gcDetails returns export function unpackChildNodesGCDetails ( gcDetails ) { const childGCDetailsMap = new Map ( ) ; if ( gcDetails . gcData === undefined ) { return childGCDetailsMap ; } const gcNodes = gcDetails . gcData . gcNodes ; delete gcNodes [ "/" ] ; for ( const [ id , outboundRoutes ] of Object . entries ( gcNodes ) ) { assert ( id . startsWith ( "/" ) , 0x2ae ) ; const childId = id . split ( "/" ) [ 1 ] ; let childGCNodeId = id . slice ( childId . length + 1 ) ; if ( childGCNodeId === "" ) { childGCNodeId = "/" ; } let childGCDetails = childGCDetailsMap . get ( childId ) ; if ( childGCDetails === undefined ) { childGCDetails = { gcData : { gcNodes : { } } , usedRoutes : [ ] } ; } assert ( childGCDetails . gcData !== undefined , 0x2af ) ; childGCDetails . gcData . gcNodes [ childGCNodeId ] = Array . from ( outboundRoutes ) ; childGCDetailsMap . set ( childId , childGCDetails ) ; } if ( gcDetails . usedRoutes === undefined ) { return childGCDetailsMap ; } const usedRoutes = gcDetails . usedRoutes . filter ( ( route ) => route !== "" && route !== "/" ) ; for ( const route of usedRoutes ) { assert ( route . startsWith ( "/" ) , 0x2b0 ) ; const childId = route . split ( "/" ) [ 1 ] ; const childUsedRoute = route . slice ( childId . length + 1 ) ; const childGCDetails = childGCDetailsMap . get ( childId ) ; assert ( childGCDetails ?. usedRoutes !== undefined , 0x2b1 ) ; childGCDetails . usedRoutes . push ( childUsedRoute ) ; childGCDetailsMap . set ( childId , childGCDetails ) ; } return childGCDetailsMap ; } param usedRoutes returns export function unpackChildNodesUsedRoutes ( usedRoutes ) { const filteredUsedRoutes = usedRoutes . filter ( ( route ) => route !== "" && route !== "/" ) ; const childUsedRoutesMap = new Map ( ) ; for ( const route of filteredUsedRoutes ) { assert ( route . startsWith ( "/" ) , 0x198 ) ; const childId = route . split ( "/" ) [ 1 ] ; const childUsedRoute = route . slice ( childId . length + 1 ) ; const childUsedRoutes = childUsedRoutesMap . get ( childId ) ; if ( childUsedRoutes !== undefined ) { childUsedRoutes . push ( childUsedRoute ) ; } else { childUsedRoutesMap . set ( childId , [ childUsedRoute ] ) ; } } return childUsedRoutesMap ; } param gcNodes param outboundRoute export function removeRouteFromAllNodes ( gcNodes , outboundRoute ) { for ( const outboundRoutes of Object . values ( gcNodes ) ) { const index = outboundRoutes . indexOf ( outboundRoute ) ; if ( index > - 1 ) { outboundRoutes . splice ( index , 1 ) ; } } } /**
 * Concatenates the given GC states and returns the concatenated GC state.
 */ export function concatGarbageCollectionStates ( gcState1 , gcState2 ) { const combinedGCNodes = { } ; for ( const [ nodeId , nodeData ] of Object . entries ( gcState1 . gcNodes ) ) { combinedGCNodes [ nodeId ] = { outboundRoutes : Array . from ( nodeData . outboundRoutes ) , unreferencedTimestampMs : nodeData . unreferencedTimestampMs } ; } for ( const [ nodeId , nodeData ] of Object . entries ( gcState2 . gcNodes ) ) { let combinedNodedata = combinedGCNodes [ nodeId ] ; if ( combinedNodedata === undefined ) { combinedNodedata = { outboundRoutes : Array . from ( nodeData . outboundRoutes ) , unreferencedTimestampMs : nodeData . unreferencedTimestampMs } ; } else { if ( nodeData . unreferencedTimestampMs !== undefined && combinedNodedata . unreferencedTimestampMs !== undefined ) { assert ( nodeData . unreferencedTimestampMs === combinedNodedata . unreferencedTimestampMs , 0x2b2 ) ; } combinedNodedata = { outboundRoutes : [ ... new Set ( [ ... nodeData . outboundRoutes , ... combinedNodedata . outboundRoutes ] ) ] , unreferencedTimestampMs : nodeData . unreferencedTimestampMs ?? combinedNodedata . unreferencedTimestampMs } ; } combinedGCNodes [ nodeId ] = combinedNodedata ; } return { gcNodes : combinedGCNodes } ; } /**
 * Concatenates the given GC datas and returns the concatenated GC data.
 */ export function concatGarbageCollectionData ( gcData1 , gcData2 ) { const combinedGCData = cloneGCData ( gcData1 ) ; for ( const [ id , routes ] of Object . entries ( gcData2 . gcNodes ) ) { if ( combinedGCData . gcNodes [ id ] === undefined ) { combinedGCData . gcNodes [ id ] = Array . from ( routes ) ; } else { const combinedRoutes = [ ... routes , ... combinedGCData . gcNodes [ id ] ] ; combinedGCData . gcNodes [ id ] = [ ... new Set ( combinedRoutes ) ] ; } } return combinedGCData ; } export class GCDataBuilder implements IGarbageCollectionData { public readonly gcNodes : { [ id ] : string [ ] ; } = { } ; public addNode ( id , outboundRoutes ) { this . gcNodes [ id ] = Array . from ( outboundRoutes ) ; } /**
     * Adds the given GC nodes. It does the following:
     * - Normalizes the ids of the given nodes.
     * - Prefixes the given `prefixId` to the given nodes' ids.
     * - Adds the outbound routes of the nodes against the normalized and prefixed id.
     */ public prefixAndAddNodes ( prefixId , gcNodes ) { for ( const [ id , outboundRoutes ] of Object . entries ( gcNodes ) ) { let normalizedId = id ; while ( normalizedId . startsWith ( "/" ) ) { normalizedId = normalizedId . substr ( 1 ) ; } normalizedId = `/${ prefixId }/${ normalizedId }` ; while ( normalizedId . endsWith ( "/" ) ) { normalizedId = normalizedId . substr ( 0 , normalizedId . length - 1 ) ; } this . gcNodes [ normalizedId ] = outboundRoutes ; } } /**
     * Adds the given outbound route to the outbound routes of all GC nodes.
     */ public addRouteToAllNodes ( outboundRoute ) { for ( const outboundRoutes of Object . values ( this . gcNodes ) ) { outboundRoutes . push ( outboundRoute ) ; } } public getGCData ( ) { return { gcNodes : this . gcNodes } ; } }