mirror of
https://github.com/bitwarden/browser
synced 2026-02-10 05:30:01 +00:00
log transmission pieces
This commit is contained in:
55
libs/common/src/tools/ring-buffer.ts
Normal file
55
libs/common/src/tools/ring-buffer.ts
Normal file
@@ -0,0 +1,55 @@
|
||||
/** A simple ring buffer; not in any way threadsafe */
|
||||
export class RingBuffer<T> {
|
||||
constructor(private capacity: number) {
|
||||
this.buffer = new Array(capacity).fill(null);
|
||||
}
|
||||
|
||||
private buffer: Array<T>;
|
||||
private head: number = 0;
|
||||
private tail: number = 0;
|
||||
private _length: number = 0;
|
||||
|
||||
/** The number of entries presently stored by the ring buffer */
|
||||
get length() {
|
||||
return this._length;
|
||||
}
|
||||
|
||||
/** `true` when the buffer is full. */
|
||||
get full() {
|
||||
return this.length === this.capacity;
|
||||
}
|
||||
|
||||
/** `true` when the buffer is empty */
|
||||
get empty() {
|
||||
return !this.length;
|
||||
}
|
||||
|
||||
/** Adds an item to the head of the buffer
|
||||
* @param value the item to add
|
||||
* @returns `true` if the item was added, otherwise `false`.
|
||||
*/
|
||||
enqueue(value: T) {
|
||||
if (this.full) {
|
||||
return false;
|
||||
}
|
||||
|
||||
this.buffer[this.head] = value;
|
||||
this.head = (this.head + 1) % this.capacity;
|
||||
this._length++;
|
||||
}
|
||||
|
||||
/** Removes the item at the tail of the buffer
|
||||
* @returns the tail item if the buffer contains any entries,
|
||||
* otherwise `undefined`.
|
||||
*/
|
||||
dequeue(): T | undefined {
|
||||
if (this.empty) {
|
||||
return undefined;
|
||||
}
|
||||
|
||||
const value = this.buffer[this.tail];
|
||||
this.tail = (this.tail + 1) % this.capacity;
|
||||
this._length--;
|
||||
return value;
|
||||
}
|
||||
}
|
||||
47
libs/common/src/tools/state/spooler.ts
Normal file
47
libs/common/src/tools/state/spooler.ts
Normal file
@@ -0,0 +1,47 @@
|
||||
import { firstValueFrom } from "rxjs";
|
||||
|
||||
import { StateProvider, UserKeyDefinition } from "../../platform/state";
|
||||
import { UserId } from "../../types/guid";
|
||||
|
||||
/** Utility for spooling data to and from append-only storage. */
|
||||
export class Spooler<T> {
|
||||
/** Instantiates a spooler
|
||||
* @param state loads and stores the spool
|
||||
* @param location where spooled records are stored
|
||||
* @param userId user performing the spooling
|
||||
*/
|
||||
constructor(
|
||||
private state: StateProvider,
|
||||
private location: UserKeyDefinition<T[]>,
|
||||
private userId: UserId,
|
||||
) {}
|
||||
|
||||
private buffer = new Array<T>();
|
||||
|
||||
/** Append a value to append-only storage */
|
||||
async spool(value: T) {
|
||||
// TODO: encrypt spooled records? Or should that be done by the calling code?
|
||||
// either way, the value pushed to `this.buffer` should be ready-to-spool.
|
||||
this.buffer.push(value);
|
||||
|
||||
await this.state.setUserState(this.location, this.buffer, this.userId);
|
||||
}
|
||||
|
||||
/** Read all values from append-only storage */
|
||||
async read(): Promise<T[]> {
|
||||
// TODO: decrypt spooled records? Or should that be done by the calling code?
|
||||
return await firstValueFrom(this.state.getUserState$(this.location, this.userId));
|
||||
}
|
||||
|
||||
/** Read all values from append-only storage */
|
||||
async unspool(): Promise<T[]> {
|
||||
const results = await this.read();
|
||||
await this.clear();
|
||||
return results;
|
||||
}
|
||||
|
||||
/** Erase append-only storage */
|
||||
async clear() {
|
||||
await this.state.setUserState(this.location, null, this.userId);
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user