import { UNSUPPORTED_BATCH_ENCODE_ARRAY_TYPE } from './constants'
import { Context } from './context'
import { Exception, SealError } from './exception'
import { MemoryPoolHandle } from './memory-pool-handle'
import { PlainText, PlainTextConstructorOptions } from './plain-text'
import { Instance, Library, LoaderOptions } from './seal'
import { VectorConstructorOptions } from './vector'
export type BatchEncoderDependencyOptions = {
readonly Exception: Exception
readonly MemoryPoolHandle: MemoryPoolHandle
readonly PlainText: PlainTextConstructorOptions
readonly Vector: VectorConstructorOptions
}
export type BatchEncoderDependencies = {
({
Exception,
MemoryPoolHandle,
PlainText,
Vector
}: BatchEncoderDependencyOptions): BatchEncoderConstructorOptions
}
export type BatchEncoderConstructorOptions = {
(context: Context): BatchEncoder
}
export type BatchEncoderTypes =
| Int32Array
| Uint32Array
| BigInt64Array
| BigUint64Array
export type BatchEncoder = {
readonly instance: Instance
readonly unsafeInject: (instance: Instance) => void
readonly delete: () => void
readonly encode: (
array: BatchEncoderTypes,
plainText?: PlainText
) => PlainText | void
readonly decode: (
plainText: PlainText,
signed?: boolean,
pool?: MemoryPoolHandle
) => Int32Array | Uint32Array
readonly decodeBigInt: (
plainText: PlainText,
signed?: boolean,
pool?: MemoryPoolHandle
) => BigInt64Array | BigUint64Array
readonly slotCount: number
}
const BatchEncoderConstructor =
(library: Library): BatchEncoderDependencies =>
({
Exception,
MemoryPoolHandle,
PlainText,
Vector
}: BatchEncoderDependencyOptions): BatchEncoderConstructorOptions =>
(context): BatchEncoder => {
const Constructor = library.BatchEncoder
let _instance: Instance
try {
_instance = new Constructor(context.instance)
} catch (e) {
throw Exception.safe(e as SealError)
}
/**
* @implements BatchEncoder
*/
/**
* @interface BatchEncoder
*/
return {
/**
* Get the underlying WASM instance
*
* @private
* @readonly
* @name BatchEncoder#instance
* @type {Instance}
*/
get instance() {
return _instance
},
/**
* Inject this object with a raw WASM instance. No type checking is performed.
*
* @private
* @function
* @name BatchEncoder#unsafeInject
* @param {Instance} instance WASM instance
*/
unsafeInject(instance: Instance) {
if (_instance) {
_instance.delete()
_instance = undefined
}
_instance = instance
},
/**
* Delete the underlying WASM instance.
*
* Should be called before dereferencing this object to prevent the
* WASM heap from growing indefinitely.
* @function
* @name BatchEncoder#delete
*/
delete() {
if (_instance) {
_instance.delete()
_instance = undefined
}
},
/**
* Creates a PlainText from a given matrix. This function "batches" a given matrix
* of either signed or unsigned integers modulo the PlainText modulus into a PlainText element, and stores
* the result in the destination parameter. The input array must have size at most equal
* to the degree of the polynomial modulus. The first half of the elements represent the
* first row of the matrix, and the second half represent the second row. The numbers
* in the matrix can be at most equal to the PlainText modulus for it to represent
* a valid PlainText.
*
* If the destination PlainText overlaps the input values in memory, the behavior of
* this function is undefined.
*
* @function
* @name BatchEncoder#encode
* @param {Int32Array|Uint32Array|BigInt64Array|BigUint64Array} array Data to encode
* @param {PlainText} [plainText=null] Destination to store the encoded result
* @returns {PlainText|void} A new PlainText holding the encoded data or void if one was provided
* @example
* import SEAL from 'node-seal'
* const seal = await SEAL()
* ...
* const batchEncoder = seal.BatchEncoder(context)
*
* const plainText = batchEncoder.encode(Int32Array.from([1, -2, 3]))
*/
encode(
array: Int32Array | Uint32Array | BigInt64Array | BigUint64Array,
plainText?: PlainText
): PlainText | void {
try {
if (array.constructor === Int32Array) {
if (plainText) {
_instance.encode(array, plainText.instance, 'INT32')
return
}
const plain = PlainText()
_instance.encode(array, plain.instance, 'INT32')
return plain
}
if (array.constructor === Uint32Array) {
if (plainText) {
_instance.encode(array, plainText.instance, 'UINT32')
return
}
const plain = PlainText()
_instance.encode(array, plain.instance, 'UINT32')
return plain
}
if (array.constructor === BigInt64Array) {
// When embind supports BigInt64Arrays we can remove this hack
const stringArray = array.toString().split(',')
if (plainText) {
_instance.encode(stringArray, plainText.instance, 'INT64')
return
}
const plain = PlainText()
_instance.encode(stringArray, plain.instance, 'INT64')
return plain
}
if (array.constructor === BigUint64Array) {
// When embind supports BigInt64Arrays we can remove this hack
const stringArray = array.toString().split(',')
if (plainText) {
_instance.encode(stringArray, plainText.instance, 'UINT64')
return
}
const plain = PlainText()
_instance.encode(stringArray, plain.instance, 'UINT64')
return plain
}
throw new Error(UNSUPPORTED_BATCH_ENCODE_ARRAY_TYPE)
} catch (e) {
throw Exception.safe(e as SealError)
}
},
/**
* Inverse of encode. This function "unbatches" a given PlainText into a matrix
* of signed or unsigned integers modulo the PlainText modulus, and stores the result in the destination
* parameter. The input PlainText must have degrees less than the polynomial modulus,
* and coefficients less than the PlainText modulus, i.e. it must be a valid PlainText
* for the encryption parameters. Dynamic memory allocations in the process are
* allocated from the memory pool pointed to by the given MemoryPoolHandle.
*
* @function
* @name BatchEncoder#decode
* @param {PlainText} plainText Data to decode
* @param {boolean} [signed=true] By default, decode as an Int32Array. If false, decode as an Uint32Array
* @param {MemoryPoolHandle} [pool={@link MemoryPoolHandle.global}]
* @returns {Int32Array|Uint32Array} TypedArray containing the decoded data
* @example
* import SEAL from 'node-seal'
* const seal = await SEAL()
* ...
* const batchEncoder = seal.BatchEncoder(context)
*
* const plainText = batchEncoder.encode(Int32Array.from([1, -2, 3]))
* const plainTextU = batchEncoder.encode(Uint32Array.from([1, 2, 3]))
*
* const result = batchEncoder.decode(plainText)
* const resultU = batchEncoder.decode(plainTextU, false) // To decode as an Uint32Array
*/
decode(
plainText: PlainText,
signed = true,
pool: MemoryPoolHandle = MemoryPoolHandle.global
): Int32Array | Uint32Array {
try {
if (signed) {
const tempVect = Vector()
const instance = _instance.decodeInt32(plainText.instance, pool)
tempVect.unsafeInject(instance)
tempVect.setType('Int32Array')
const tempArr = tempVect.toArray() as Int32Array
tempVect.delete()
return tempArr
}
const tempVect = Vector()
const instance = _instance.decodeUint32(plainText.instance, pool)
tempVect.unsafeInject(instance)
tempVect.setType('Uint32Array')
const tempArr = tempVect.toArray() as Uint32Array
tempVect.delete()
return tempArr
} catch (e) {
throw Exception.safe(e as SealError)
}
},
/**
* Performs the same function as the 32-bit decode, but supports true
* 64-bit values encapsulated by a BigInt.
*
* There's no official support for sending a BigInt64Array/BigUint64Array
* from C++ to JS, therefore this function uses string conversion to
* marshal data which is noticably slower. Use this function if you
* absolutely need to marshal values larger than 32 bits.
*
* @see {@link BatchEncoder#decode} for more information about decode.
* @function
* @name BatchEncoder#decodeBigInt
* @param {PlainText} plainText Data to decode
* @param {boolean} [signed=true] By default, decode as an BigInt64Array. If false, decode as an BigUint64Array
* @param {MemoryPoolHandle} [pool={@link MemoryPoolHandle.global}]
* @returns {BigInt64Array|BigUint64Array} TypedArray containing the decoded data
* @example
* import SEAL from 'node-seal'
* const seal = await SEAL()
* ...
* const batchEncoder = seal.BatchEncoder(context)
*
* const plainText = batchEncoder.encode(BigInt64Array.from([1n, -2n, 3n]))
* const plainTextU = batchEncoder.encode(BigUint64Array.from([1n, 2n, 3n]))
*
* const result = batchEncoder.decodeBigInt(plainText)
* const resultU = batchEncoder.decodeBigInt(plainTextU, false) // To decode as an BigUint64Array
*/
decodeBigInt(
plainText: PlainText,
signed = true,
pool: MemoryPoolHandle = MemoryPoolHandle.global
): BigInt64Array | BigUint64Array {
try {
if (signed) {
const instance = _instance.decodeBigInt(
plainText.instance,
true,
pool
)
return BigInt64Array.from(instance)
}
const instance = _instance.decodeBigInt(
plainText.instance,
false,
pool
)
return BigUint64Array.from(instance)
} catch (e) {
throw Exception.safe(e as SealError)
}
},
/**
* The total number of batching slots available to hold data
*
* @readonly
* @name BatchEncoder#slotCount
* @type {number}
*/
get slotCount() {
return _instance.slotCount()
}
}
}
export const BatchEncoderInit = ({
loader
}: LoaderOptions): BatchEncoderDependencies => {
const library: Library = loader.library
return BatchEncoderConstructor(library)
}
Source