"use strict";
/*!
 * Copyright (c) Microsoft Corporation and contributors. All rights reserved.
 * Licensed under the MIT License.
 */
Object.defineProperty(exports, "__esModule", { value: true });
exports.OpCompressor = void 0;
const client_utils_1 = require("@fluid-internal/client-utils");
const internal_1 = require("@fluidframework/core-utils/internal");
const internal_2 = require("@fluidframework/telemetry-utils/internal");
const lz4js_1 = require("lz4js");
const compressionDefinitions_js_1 = require("../compressionDefinitions.js");
const outbox_js_1 = require("./outbox.js");
/**
 * Compresses batches of ops.
 *
 * @remarks Only single-message batches are supported
 * Use opGroupingManager to group a batch into a singleton batch suitable for compression.
 */
class OpCompressor {
    constructor(logger) {
        this.logger = (0, internal_2.createChildLogger)({ logger, namespace: "OpCompressor" });
    }
    /**
     * Combines the contents of the singleton batch into a single JSON string and compresses it, putting
     * the resulting string as the message contents in place of the original uncompressed payload.
     * @param batch - The batch to compress. Must have only 1 message
     * @returns A singleton batch containing a single compressed message
     */
    compressBatch(batch) {
        (0, internal_1.assert)(batch.contentSizeInBytes > 0 && batch.messages.length === 1, 0x5a4 /* Batch should not be empty and should contain a single message */);
        const compressionStart = Date.now();
        const contentsAsBuffer = new TextEncoder().encode(this.serializeBatchContents(batch));
        const compressedContents = (0, lz4js_1.compress)(contentsAsBuffer);
        const compressedContent = client_utils_1.IsoBuffer.from(compressedContents).toString("base64");
        const duration = Date.now() - compressionStart;
        const messages = [
            {
                ...batch.messages[0],
                contents: JSON.stringify({ packedContents: compressedContent }),
                metadata: batch.messages[0].metadata,
                compression: compressionDefinitions_js_1.CompressionAlgorithms.lz4,
            },
        ];
        const compressedBatch = {
            contentSizeInBytes: compressedContent.length,
            messages,
            referenceSequenceNumber: batch.referenceSequenceNumber,
        };
        if (batch.contentSizeInBytes > 200000) {
            this.logger.sendPerformanceEvent({
                eventName: "CompressedBatch",
                duration,
                sizeBeforeCompression: batch.contentSizeInBytes,
                sizeAfterCompression: compressedBatch.contentSizeInBytes,
                opCount: compressedBatch.messages.length,
                socketSize: (0, outbox_js_1.estimateSocketSize)(compressedBatch),
            });
        }
        return compressedBatch;
    }
    /**
     * Combine the batch's content strings into a single JSON string (a serialized array)
     */
    serializeBatchContents(batch) {
        const [message, ...none] = batch.messages;
        (0, internal_1.assert)(none.length === 0, 0xb78 /* Batch should only contain a single message */);
        try {
            // This is expressed as a JSON array, for legacy reasons
            return `[${message.contents}]`;
        }
        catch (error) {
            if (error.message === "Invalid string length") {
                // This is how string interpolation signals that
                // the content size exceeds its capacity
                const dpe = internal_2.DataProcessingError.create("Payload too large", "OpCompressor");
                this.logger.sendErrorEvent({
                    eventName: "BatchTooLarge",
                    size: batch.contentSizeInBytes,
                    length: batch.messages.length,
                }, dpe);
                throw dpe;
            }
            throw error;
        }
    }
}
exports.OpCompressor = OpCompressor;
//# sourceMappingURL=opCompressor.js.map