/* * Copyright 2021 Juan Cruz Viotti * * Licensed 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. */ import { strict as assert } from 'assert' import ResizableBuffer from '../resizable-buffer' import { JSONValue } from '../../json' import { UINT8_MAX } from '../../utils/limits' import { encode } from '../index' import { Encoding } from '../../mapper' import { TypedBoundedOptions, TypedFloorOptions, TypedRoofOptions, SizeTypedOptions } from './options' import { FLOOR_MULTIPLE_ENUM_VARINT, BOUNDED_MULTIPLE_8BITS_ENUM_FIXED, ROOF_MULTIPLE_MIRROR_ENUM_VARINT } from '../integer/encode' import { EncodingContext } from '../context' const encodeArray = ( buffer: ResizableBuffer, offset: number, value: JSONValue[], prefixEncodings: Encoding[], context: EncodingContext, defaultEncoding: Encoding ): number => { let cursor = offset for (const [ index, element ] of value.entries()) { const encoding: Encoding | undefined = prefixEncodings[index] ?? defaultEncoding const bytesWritten: number = encode(buffer, cursor, encoding, element, context) cursor += bytesWritten } return cursor - offset } export const FIXED_TYPED_ARRAY = ( buffer: ResizableBuffer, offset: number, value: JSONValue[], options: SizeTypedOptions, context: EncodingContext ): number => { assert(options.size >= 0) return encodeArray(buffer, offset, value, options.prefixEncodings, context, options.encoding) } export const BOUNDED_TYPED_LENGTH_PREFIX = ( buffer: ResizableBuffer, offset: number, value: JSONValue[], options: TypedBoundedOptions, context: EncodingContext ): number => { assert(options.maximum >= 0) assert(options.minimum >= 0) assert(options.maximum >= options.minimum) assert(value.length >= options.minimum) assert(value.length <= options.maximum) // TODO: Map cases where maximum == minimum to FIXED_TYPED_ARRAY in the mapper // instead of handling it here const lengthBytes: number = options.maximum === options.minimum ? 0 : FLOOR_MULTIPLE_ENUM_VARINT(buffer, offset, value.length, { minimum: options.minimum, multiplier: 1 }, context) return lengthBytes + encodeArray( buffer, offset + lengthBytes, value, options.prefixEncodings, context, options.encoding) } export const BOUNDED_8BITS_TYPED_LENGTH_PREFIX = ( buffer: ResizableBuffer, offset: number, value: JSONValue[], options: TypedBoundedOptions, context: EncodingContext ): number => { assert(options.maximum >= 0) assert(options.minimum >= 0) assert(options.maximum >= options.minimum) assert(value.length >= options.minimum) assert(value.length <= options.maximum) assert(options.maximum - options.minimum <= UINT8_MAX) // TODO: Map cases where maximum == minimum to FIXED_TYPED_ARRAY in the mapper // instead of handling it here const lengthBytes: number = options.maximum === options.minimum ? 0 : BOUNDED_MULTIPLE_8BITS_ENUM_FIXED(buffer, offset, value.length, { minimum: options.minimum, maximum: options.maximum, multiplier: 1 }, context) return lengthBytes + encodeArray( buffer, offset + lengthBytes, value, options.prefixEncodings, context, options.encoding) } export const ROOF_TYPED_LENGTH_PREFIX = ( buffer: ResizableBuffer, offset: number, value: JSONValue[], options: TypedRoofOptions, context: EncodingContext ): number => { assert(options.maximum >= 0) assert(value.length <= options.maximum) const lengthBytes: number = ROOF_MULTIPLE_MIRROR_ENUM_VARINT(buffer, offset, value.length, { maximum: options.maximum, multiplier: 1 }, context) return lengthBytes + encodeArray( buffer, offset + lengthBytes, value, options.prefixEncodings, context, options.encoding) } export const FLOOR_TYPED_LENGTH_PREFIX = ( buffer: ResizableBuffer, offset: number, value: JSONValue[], options: TypedFloorOptions, context: EncodingContext ): number => { assert(options.minimum >= 0) assert(value.length >= options.minimum) const lengthBytes: number = FLOOR_MULTIPLE_ENUM_VARINT(buffer, offset, value.length, { minimum: options.minimum, multiplier: 1 }, context) return lengthBytes + encodeArray( buffer, offset + lengthBytes, value, options.prefixEncodings, context, options.encoding) }