189 lines
6.9 KiB
JavaScript
189 lines
6.9 KiB
JavaScript
|
||
/*==============================================================================================================
|
||
*
|
||
* CircQueue_t
|
||
*
|
||
* =============================================================================================================
|
||
* 描述:
|
||
* 这是一个环形队列, 支持入队、出队、丢弃数据等操作。
|
||
* 支持深拷贝入队数据,避免外部修改影响队列内部数据。
|
||
* 支持最大内存限制,超过限制则拒绝入队。
|
||
* 支持异步入队和出队操作,适用于异步场景。
|
||
* 支持 JSON 序列化,方便调试和存储。
|
||
* 注意事项:
|
||
* 1. 队列满时入队会失败,需处理返回值
|
||
* 2. 出队时返回深拷贝对象,避免修改影响队列
|
||
* 3. 异步操作时需注意超时处理,避免死循环
|
||
* 4. 仅支持可序列化对象, 深拷贝基于 JSON 方法,可能无法处理函数、循环引用等复杂对象
|
||
* 5. 内存限制基于估算的 JSON 字符串长度
|
||
* 示例:
|
||
* const queue = new CircQueue(5, { deepCopy: true, maxMemoryBytes: 1024 }); // 创建容量为5的队列,启用深拷贝,最大内存限制1KB
|
||
* queue.enter({ name: "Alice" }); // 入队一个对象
|
||
* const item = queue.out(); // 出队一个对象,返回深拷贝
|
||
* queue.discard(2); // 丢弃队尾2个对象
|
||
* queue.send({ msg: "async item" }, 1000); // 异步入队,超时1秒
|
||
* queue.recv(1000); // 异步出队,超时1秒
|
||
* console.log(JSON.stringify(queue)); // 序列化队列内容
|
||
*=============================================================================================================*/
|
||
class CircQueue
|
||
{
|
||
/**
|
||
* 构造函数
|
||
* capacity :队列可容纳的最大项目数
|
||
* options :可选的配置对象,用于自定义环形队列的行为。它支持以下配置项:
|
||
* deepCopy :控制入队时是否深拷贝元素, 默认 true
|
||
* maxMemoryBytes :队列的最大内存限制 (单位:字节),默认 Infinity, 不限制内存
|
||
* 若传 1024 * 1024 代表限制 1 M ,超出则队满, 队列会拒绝入队
|
||
*/
|
||
constructor(capacity, options = {})
|
||
{
|
||
this.capacity = capacity; /* 队列容量 (最多存储项目数) */
|
||
this.buffer = new Array(capacity); /* 队列存储区,使用数组存任意对象 */
|
||
this.head = 0; /* 队头索引 */
|
||
this.tail = 0; /* 队尾索引 */
|
||
this.isFull = false; /* 队满标志 */
|
||
|
||
this.deepCopy = options.deepCopy ?? true; /* 是否启用深拷贝 (默认启用) */
|
||
this.maxMemoryBytes = options.maxMemoryBytes ?? Infinity; /* 队列最大内存限制 (单位:字节) */
|
||
this.currentMemoryBytes = 0; /* 当前已使用内存大小 (单位:字节) */
|
||
}
|
||
|
||
/* 判断队列是否为空 */
|
||
isEmpty()
|
||
{
|
||
return this.head === this.tail && !this.isFull;
|
||
}
|
||
|
||
/* 判断队列是否已满 */
|
||
isFullFn()
|
||
{
|
||
return this.isFull;
|
||
}
|
||
|
||
/* 估算对象大小 (以 JSON 字符串的 UTF-8 编码长度作为近似值) */
|
||
_estimateSize(obj)
|
||
{
|
||
try
|
||
{
|
||
return Buffer.byteLength(JSON.stringify(obj), 'utf8'); /* Node.js 中用于计算字节长度 */
|
||
}
|
||
catch
|
||
{
|
||
return 0; /* 若对象不可序列化,返回 0 (避免出错) */
|
||
}
|
||
}
|
||
|
||
/* 深拷贝对象 (基于 JSON 的方式,仅支持可序列化对象) */
|
||
_deepClone(obj)
|
||
{
|
||
return this.deepCopy ? JSON.parse(JSON.stringify(obj)) : obj;
|
||
}
|
||
|
||
/* 入队操作:将对象放入队尾 */
|
||
enter(item)
|
||
{
|
||
const itemSize = this._estimateSize(item); /* 估算项目内存大小 */
|
||
if (this.isFullFn() || (this.currentMemoryBytes + itemSize > this.maxMemoryBytes))
|
||
{
|
||
return false; /* 若队满或超过内存限制则入队失败 */
|
||
}
|
||
|
||
const clone = this._deepClone(item); /* 深拷贝入队数据 (避免外部修改影响队列) */
|
||
this.buffer[this.tail] = clone; /* 存入队尾 */
|
||
this.tail = (this.tail + 1) % this.capacity; /* 环形增长 tail */
|
||
this.currentMemoryBytes += itemSize; /* 增加当前内存使用 */
|
||
|
||
if (this.tail === this.head) this.isFull = true; /* 若尾追头,表示队满 */
|
||
return true;
|
||
}
|
||
|
||
/* 出队操作:从队头取出一个对象, 返回深拷贝 */
|
||
out()
|
||
{
|
||
if (this.isEmpty()) return null; /* 队空返回 null */
|
||
|
||
const item = this.buffer[this.head]; /* 读取队头项目 */
|
||
this.currentMemoryBytes -= this._estimateSize(item); /* 更新内存使用 */
|
||
this.head = (this.head + 1) % this.capacity; /* 环形增长 head */
|
||
this.isFull = false; /* 出队必然队不满 */
|
||
return this._deepClone(item); /* 返回深拷贝,保护队列内部数据 */
|
||
}
|
||
|
||
/* 丢弃数据:从队尾向前丢弃 len 项 (反向移动 tail) */
|
||
discard(len)
|
||
{
|
||
for (let i = 0; i < len; i++)
|
||
{
|
||
if (this.isEmpty()) return false; /* 队空则不能继续丢弃 */
|
||
|
||
/* 定位最后一项 */
|
||
const item = this.buffer[(this.tail - 1 + this.capacity) % this.capacity];
|
||
this.currentMemoryBytes -= this._estimateSize(item); /* 减去内存大小 */
|
||
this.tail = (this.tail - 1 + this.capacity) % this.capacity; /* tail 回退 */
|
||
this.isFull = false;
|
||
}
|
||
return true;
|
||
}
|
||
|
||
/* 获取当前队列中元素个数 */
|
||
getDepth()
|
||
{
|
||
return this.isFull
|
||
? this.capacity
|
||
: (this.tail + this.capacity - this.head) % this.capacity;
|
||
}
|
||
|
||
/**
|
||
* 异步入队,支持超时退出,不会阻塞主线程, 可用于异步场景
|
||
* buf : 需要发送的数据
|
||
* timeoutMs : 超时时间
|
||
*/
|
||
async send(item, timeoutMs = 0)
|
||
{
|
||
const start = Date.now();
|
||
|
||
/* 循环直到成功入队或超时 */
|
||
while (!this.enter(item))
|
||
{
|
||
if (Date.now() - start >= timeoutMs) return false; /* 超时退出 */
|
||
await new Promise(resolve => setTimeout(resolve, 1)); /* 异步轮询, 自旋加 sleep 挂起 1ms */
|
||
}
|
||
return true;
|
||
}
|
||
|
||
/**
|
||
* 异步出队,支持超时退出,不会阻塞主线程, 可用于异步场景
|
||
* buf : 需要发送的数据
|
||
* timeoutMs : 超时时间
|
||
* 返回值:成功取出一个对象(深拷贝)或 null (超时)
|
||
*/
|
||
async recv(timeoutMs = 0)
|
||
{
|
||
const start = Date.now();
|
||
while (true)
|
||
{
|
||
const item = this.out();
|
||
if (item !== null) return item; /* 成功取出则返回 */
|
||
if (Date.now() - start >= timeoutMs) return null; /* 超时退出 */
|
||
await new Promise(resolve => setTimeout(resolve, 1)); /* 异步轮询, 自旋加 sleep 挂起 1ms */
|
||
}
|
||
}
|
||
|
||
/* JSON 序列化接口,用于 JSON.stringify(queue) */
|
||
toJSON()
|
||
{
|
||
const result = [];
|
||
let i = this.head;
|
||
let count = this.getDepth();
|
||
while (count-- > 0)
|
||
{
|
||
result.push(this._deepClone(this.buffer[i])); /* 复制每个元素 */
|
||
i = (i + 1) % this.capacity; /* 移动到下一个元素 */
|
||
}
|
||
return result;
|
||
}
|
||
}
|
||
export default CircQueue;
|
||
// module.exports = CircQueue;
|
||
|