@litert/televoke
Version:
A simple RPC service framework.
143 lines (104 loc) • 4 kB
text/typescript
/**
* Copyright 2025 Angus.Fenying <fenying@litert.org>
*
* 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
*
* https://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 * as Shared from './Shared.decl';
import * as Constants from './Constants';
import * as E from './Errors';
import { TvBinaryReadStream } from './BinaryStream';
export class TvBinaryStreamManager implements Shared.IStreamManager {
private _closed: boolean = false;
private readonly _streams: Record<string, TvBinaryReadStream> = {};
public constructor(
public defaultTimeout: number,
public maxStreams: number,
) { }
private _generateStreamId(): number {
do {
const id = Math.floor(Math.random() * 0xFFFF_FFFF);
if (id in this._streams) {
continue;
}
return id;
} while (true);
}
public close(): void {
this._closed = true;
for (const id of Object.keys(this._streams)) {
this._streams[id].abort();
delete this._streams[id];
}
}
public get(id: number): Shared.IBinaryReadStream | null {
return this._streams[id] ?? null;
}
public create(): Shared.IBinaryReadStream {
if (this.maxStreams === 0) {
throw new E.errors.cmd_not_impl();
}
else if (this._closed) {
throw new E.errors.stream_closed();
}
else if (this.maxStreams >= 0 && Object.keys(this._streams).length === this.maxStreams) {
throw new E.errors.system_busy();
}
const stream = new TvBinaryReadStream(this._generateStreamId(), this.defaultTimeout);
this._streams[stream.id] = stream.on('close', () => {
delete this._streams[stream.id];
});
return stream;
}
}
/**
* Create a factory function to create a `IStreamManager` instance, associated to a single channel.
* When the associated channel is closed, all streams insides the stream manager will be aborted.
*
* @param defaultTimeout The default timeout value of each new binary stream.
* @returns The factory function.
*/
export function createChannelStreamManagerFactory(
defaultTimeout: number = Constants.DEFAULT_STREAM_TIMEOUT,
maxStreams: number = Constants.DEFAULT_MAX_STREAMS,
): Shared.IStreamManagerFactory {
return (ch) => {
const ret = new TvBinaryStreamManager(defaultTimeout, maxStreams);
ch.on('close', () => { ret.close(); });
return ret;
};
}
/**
* Create a factory function to create a `IStreamManager` instance, shared between all channels.
* So that the streams created could be used in different channels.
*
* @param defaultTimeout The default timeout value of each new binary stream.
* @returns The factory function.
*/
export function createSharedStreamManagerFactory(
defaultTimeout: number = Constants.DEFAULT_STREAM_TIMEOUT,
maxStreams: number = Constants.DEFAULT_MAX_STREAMS,
): Shared.IStreamManagerFactory {
const ret = new TvBinaryStreamManager(defaultTimeout, maxStreams);
return () => ret;
}
/**
* Create a factory function to create a `IStreamManager` instance, disabling all binary streams.
*
* > This is useful when you don't need binary streams.
*
* @returns The factory function.
*/
export function createDisabledStreamManagerFactory(): Shared.IStreamManagerFactory {
const mgr = new TvBinaryStreamManager(0, 0);
return () => mgr;
}