blob: 12154565bdf3c5389e0b339a4bee2245c0b98e6d [file]
/*
* Licensed to the Apache Software Foundation (ASF) under one
* or more contributor license agreements. See the NOTICE file
* distributed with this work for additional information
* regarding copyright ownership. The ASF licenses this file
* to you under the Apache License, Version 2.0 (the
* "License"); you may not use this file except in compliance
* with the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing,
* software distributed under the License is distributed on an
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
* KIND, either express or implied. See the License for the
* specific language governing permissions and limitations
* under the License.
*/
/**
* Budgets a page of plain protocol projections without encoding its accepted
* items again. The template must contain the empty item array and a null cursor
* (or nextOffset); all other fields must stay unchanged while assembling a page.
* Accepted items must also remain unchanged. This counts a result, not a frame.
*/
export class JsonArrayPageBudget {
readonly #envelopeBytes: number;
#itemsBytes = 0;
#count = 0;
constructor(
private readonly maxBytes: number,
emptyPage: object,
) {
// The template already includes array brackets and the cursor's field name.
this.#envelopeBytes = jsonBytes(emptyPage) - 'null'.length;
}
tryAppend(item: unknown, cursor: unknown): boolean {
// JSON array elements that encode as undefined are represented by null.
const itemBytes = Buffer.byteLength(JSON.stringify(item) ?? 'null', 'utf8');
const candidateBytes = this.#itemsBytes + (this.#count > 0 ? 1 : 0) + itemBytes;
if (this.#envelopeBytes + candidateBytes + jsonBytes(cursor) > this.maxBytes) return false;
this.#itemsBytes = candidateBytes;
this.#count += 1;
return true;
}
}
function jsonBytes(value: unknown): number {
return Buffer.byteLength(JSON.stringify(value), 'utf8');
}