"use strict";
/*!
 * Copyright (c) Microsoft Corporation and contributors. All rights reserved.
 * Licensed under the MIT License.
 */
Object.defineProperty(exports, "__esModule", { value: true });
exports.sequenceNumbersMatch = exports.estimateSocketSize = exports.BatchManager = void 0;
/**
 * Estimated size of the stringification overhead for an op accumulated
 * from runtime to loader to the service.
 */
const opOverhead = 200;
/**
 * Helper class that manages partial batch & rollback.
 */
class BatchManager {
    options;
    pendingBatch = [];
    batchContentSize = 0;
    hasReentrantOps = false;
    get length() {
        return this.pendingBatch.length;
    }
    get contentSizeInBytes() {
        return this.batchContentSize;
    }
    get sequenceNumbers() {
        return {
            referenceSequenceNumber: this.referenceSequenceNumber,
            clientSequenceNumber: this.clientSequenceNumber,
        };
    }
    get referenceSequenceNumber() {
        return this.pendingBatch.length === 0
            ? undefined
            : this.pendingBatch[this.pendingBatch.length - 1].referenceSequenceNumber;
    }
    clientSequenceNumber;
    constructor(options) {
        this.options = options;
    }
    push(message, reentrant, currentClientSequenceNumber) {
        const contentSize = this.batchContentSize + (message.contents?.length ?? 0);
        const opCount = this.pendingBatch.length;
        this.hasReentrantOps = this.hasReentrantOps || reentrant;
        // Attempt to estimate batch size, aka socket message size.
        // Each op has pretty large envelope, estimating to be 200 bytes.
        // Also content will be strigified, and that adds a lot of overhead due to a lot of escape characters.
        // Not taking it into account, as compression work should help there - compressed payload will be
        // initially stored as base64, and that requires only 2 extra escape characters.
        const socketMessageSize = contentSize + opOverhead * opCount;
        if (socketMessageSize >= this.options.hardLimit) {
            return false;
        }
        if (this.pendingBatch.length === 0) {
            this.clientSequenceNumber = currentClientSequenceNumber;
        }
        this.batchContentSize = contentSize;
        this.pendingBatch.push(message);
        return true;
    }
    get empty() {
        return this.pendingBatch.length === 0;
    }
    popBatch() {
        const batch = {
            content: this.pendingBatch,
            contentSizeInBytes: this.batchContentSize,
            referenceSequenceNumber: this.referenceSequenceNumber,
            hasReentrantOps: this.hasReentrantOps,
        };
        this.pendingBatch = [];
        this.batchContentSize = 0;
        this.clientSequenceNumber = undefined;
        this.hasReentrantOps = false;
        return addBatchMetadata(batch);
    }
    /**
     * Capture the pending state at this point
     */
    checkpoint() {
        const startPoint = this.pendingBatch.length;
        return {
            rollback: (process) => {
                for (let i = this.pendingBatch.length; i > startPoint;) {
                    i--;
                    const message = this.pendingBatch[i];
                    this.batchContentSize -= message.contents?.length ?? 0;
                    process(message);
                }
                this.pendingBatch.length = startPoint;
            },
        };
    }
}
exports.BatchManager = BatchManager;
const addBatchMetadata = (batch) => {
    if (batch.content.length > 1) {
        batch.content[0].metadata = {
            ...batch.content[0].metadata,
            batch: true,
        };
        batch.content[batch.content.length - 1].metadata = {
            ...batch.content[batch.content.length - 1].metadata,
            batch: false,
        };
    }
    return batch;
};
/**
 * Estimates the real size in bytes on the socket for a given batch. It assumes that
 * the envelope size (and the size of an empty op) is 200 bytes, taking into account
 * extra overhead from stringification.
 *
 * @param batch - the batch to inspect
 * @returns An estimate of the payload size in bytes which will be produced when the batch is sent over the wire
 */
const estimateSocketSize = (batch) => {
    return batch.contentSizeInBytes + opOverhead * batch.content.length;
};
exports.estimateSocketSize = estimateSocketSize;
const sequenceNumbersMatch = (seqNums, otherSeqNums) => {
    return ((seqNums.referenceSequenceNumber === undefined ||
        otherSeqNums.referenceSequenceNumber === undefined ||
        seqNums.referenceSequenceNumber === otherSeqNums.referenceSequenceNumber) &&
        (seqNums.clientSequenceNumber === undefined ||
            otherSeqNums.clientSequenceNumber === undefined ||
            seqNums.clientSequenceNumber === otherSeqNums.clientSequenceNumber));
};
exports.sequenceNumbersMatch = sequenceNumbersMatch;
//# sourceMappingURL=batchManager.js.map