@peerbit/shared-log 9.1.2 → 9.2.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/benchmark/get-samples.js +2 -3
- package/dist/benchmark/get-samples.js.map +1 -1
- package/dist/benchmark/index.js +4 -6
- package/dist/benchmark/index.js.map +1 -1
- package/dist/benchmark/memory/child.d.ts +2 -0
- package/dist/benchmark/memory/child.d.ts.map +1 -0
- package/dist/benchmark/memory/child.js +149 -0
- package/dist/benchmark/memory/child.js.map +1 -0
- package/dist/benchmark/memory/index.d.ts +2 -0
- package/dist/benchmark/memory/index.d.ts.map +1 -0
- package/dist/benchmark/memory/index.js +81 -0
- package/dist/benchmark/memory/index.js.map +1 -0
- package/dist/benchmark/memory/utils.d.ts +13 -0
- package/dist/benchmark/memory/utils.d.ts.map +1 -0
- package/dist/benchmark/memory/utils.js +2 -0
- package/dist/benchmark/memory/utils.js.map +1 -0
- package/dist/benchmark/replication-prune.js +27 -25
- package/dist/benchmark/replication-prune.js.map +1 -1
- package/dist/benchmark/replication.js +15 -16
- package/dist/benchmark/replication.js.map +1 -1
- package/dist/src/debounce.d.ts +25 -0
- package/dist/src/debounce.d.ts.map +1 -0
- package/dist/src/debounce.js +130 -0
- package/dist/src/debounce.js.map +1 -0
- package/dist/src/index.d.ts +55 -21
- package/dist/src/index.d.ts.map +1 -1
- package/dist/src/index.js +867 -390
- package/dist/src/index.js.map +1 -1
- package/dist/src/pid.d.ts.map +1 -1
- package/dist/src/pid.js +23 -21
- package/dist/src/pid.js.map +1 -1
- package/dist/src/ranges.d.ts +104 -8
- package/dist/src/ranges.d.ts.map +1 -1
- package/dist/src/ranges.js +518 -76
- package/dist/src/ranges.js.map +1 -1
- package/dist/src/replication-domain-hash.d.ts.map +1 -1
- package/dist/src/replication-domain-hash.js.map +1 -1
- package/dist/src/replication-domain-time.d.ts.map +1 -1
- package/dist/src/replication-domain-time.js.map +1 -1
- package/dist/src/replication-domain.d.ts +22 -2
- package/dist/src/replication-domain.d.ts.map +1 -1
- package/dist/src/replication-domain.js +33 -0
- package/dist/src/replication-domain.js.map +1 -1
- package/dist/src/replication.d.ts +1 -55
- package/dist/src/replication.d.ts.map +1 -1
- package/dist/src/replication.js +5 -215
- package/dist/src/replication.js.map +1 -1
- package/dist/src/role.d.ts +1 -0
- package/dist/src/role.d.ts.map +1 -1
- package/dist/src/role.js +1 -0
- package/dist/src/role.js.map +1 -1
- package/dist/src/utils.d.ts +6 -0
- package/dist/src/utils.d.ts.map +1 -0
- package/dist/src/utils.js +39 -0
- package/dist/src/utils.js.map +1 -0
- package/package.json +5 -5
- package/src/debounce.ts +172 -0
- package/src/index.ts +1282 -562
- package/src/pid.ts +27 -25
- package/src/ranges.ts +794 -181
- package/src/replication-domain-hash.ts +3 -1
- package/src/replication-domain-time.ts +2 -1
- package/src/replication-domain.ts +68 -5
- package/src/replication.ts +9 -235
- package/src/role.ts +1 -0
- package/src/utils.ts +49 -0
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
import { BinaryReader, BinaryWriter } from "@dao-xyz/borsh";
|
|
2
2
|
import { sha256 } from "@peerbit/crypto";
|
|
3
3
|
import type { ShallowOrFullEntry } from "@peerbit/log";
|
|
4
|
+
import type { EntryReplicated } from "./ranges.js";
|
|
4
5
|
import {
|
|
5
6
|
type Log,
|
|
6
7
|
type ReplicationDomain,
|
|
@@ -15,7 +16,7 @@ export const hashToU32 = (hash: Uint8Array) => {
|
|
|
15
16
|
};
|
|
16
17
|
|
|
17
18
|
const hashTransformer: ReplicationDomainMapper<any> = async (
|
|
18
|
-
entry: ShallowOrFullEntry<any
|
|
19
|
+
entry: ShallowOrFullEntry<any> | EntryReplicated,
|
|
19
20
|
) => {
|
|
20
21
|
// For a fixed set or members, the choosen leaders will always be the same (address invariant)
|
|
21
22
|
// This allows for that same content is always chosen to be distributed to same peers, to remove unecessary copies
|
|
@@ -29,6 +30,7 @@ const hashTransformer: ReplicationDomainMapper<any> = async (
|
|
|
29
30
|
// convert hash of slot to a number
|
|
30
31
|
return hashToU32(seed);
|
|
31
32
|
};
|
|
33
|
+
|
|
32
34
|
export type ReplicationDomainHash = ReplicationDomain<undefined, any>;
|
|
33
35
|
export const createReplicationDomainHash: () => ReplicationDomainHash = () => {
|
|
34
36
|
return {
|
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
import type { ShallowOrFullEntry } from "@peerbit/log";
|
|
2
|
+
import type { EntryReplicated } from "./ranges.js";
|
|
2
3
|
import {
|
|
3
4
|
type ReplicationDomain,
|
|
4
5
|
type ReplicationDomainMapper,
|
|
@@ -27,7 +28,7 @@ export const fromEntry = (
|
|
|
27
28
|
const scalar = scalarNanoToUnit[unit];
|
|
28
29
|
const originTime = +origin / scalarMilliToUnit[unit];
|
|
29
30
|
|
|
30
|
-
const fn = (entry: ShallowOrFullEntry<any>) => {
|
|
31
|
+
const fn = (entry: ShallowOrFullEntry<any> | EntryReplicated) => {
|
|
31
32
|
const cursor = entry.meta.clock.timestamp.wallTime / scalar;
|
|
32
33
|
return Math.round(Number(cursor) - originTime);
|
|
33
34
|
};
|
|
@@ -1,15 +1,14 @@
|
|
|
1
1
|
import type { PublicSignKey } from "@peerbit/crypto";
|
|
2
2
|
import { type Index } from "@peerbit/indexer-interface";
|
|
3
3
|
import type { Entry, ShallowEntry } from "@peerbit/log";
|
|
4
|
-
import
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
} from "./replication.js";
|
|
4
|
+
import { debounceAcculmulator } from "./debounce.js";
|
|
5
|
+
import type { EntryReplicated, ReplicationRangeIndexable } from "./ranges.js";
|
|
6
|
+
import type { ReplicationLimits } from "./replication.js";
|
|
8
7
|
import { MAX_U32 } from "./role.js";
|
|
9
8
|
|
|
10
9
|
export type u32 = number;
|
|
11
10
|
export type ReplicationDomainMapper<T> = (
|
|
12
|
-
entry: Entry<T> | ShallowEntry,
|
|
11
|
+
entry: Entry<T> | ShallowEntry | EntryReplicated,
|
|
13
12
|
) => Promise<u32> | u32;
|
|
14
13
|
|
|
15
14
|
export type Log = {
|
|
@@ -33,6 +32,64 @@ type CoverRange = {
|
|
|
33
32
|
offset: number | PublicSignKey;
|
|
34
33
|
length?: number;
|
|
35
34
|
};
|
|
35
|
+
export type ReplicationChanges = ReplicationChange[];
|
|
36
|
+
export type ReplicationChange =
|
|
37
|
+
| {
|
|
38
|
+
type: "added";
|
|
39
|
+
range: ReplicationRangeIndexable;
|
|
40
|
+
}
|
|
41
|
+
| {
|
|
42
|
+
type: "removed";
|
|
43
|
+
range: ReplicationRangeIndexable;
|
|
44
|
+
}
|
|
45
|
+
| {
|
|
46
|
+
type: "updated";
|
|
47
|
+
range: ReplicationRangeIndexable;
|
|
48
|
+
prev: ReplicationRangeIndexable;
|
|
49
|
+
};
|
|
50
|
+
|
|
51
|
+
export const mergeReplicationChanges = (
|
|
52
|
+
changes: ReplicationChanges | ReplicationChanges[],
|
|
53
|
+
): ReplicationChanges => {
|
|
54
|
+
let first = changes[0];
|
|
55
|
+
if (!Array.isArray(first)) {
|
|
56
|
+
return changes as ReplicationChanges;
|
|
57
|
+
}
|
|
58
|
+
return (changes as ReplicationChanges[]).flat();
|
|
59
|
+
};
|
|
60
|
+
|
|
61
|
+
export const debounceAggregationChanges = (
|
|
62
|
+
fn: (changeOrChanges: ReplicationChange[]) => void,
|
|
63
|
+
delay: number,
|
|
64
|
+
) => {
|
|
65
|
+
return debounceAcculmulator(
|
|
66
|
+
(result) => {
|
|
67
|
+
return fn([...result.values()]);
|
|
68
|
+
},
|
|
69
|
+
() => {
|
|
70
|
+
let aggregated: Map<string, ReplicationChange> = new Map();
|
|
71
|
+
return {
|
|
72
|
+
add: (change: ReplicationChange) => {
|
|
73
|
+
const prev = aggregated.get(change.range.idString);
|
|
74
|
+
if (prev) {
|
|
75
|
+
if (prev.range.timestamp < change.range.timestamp) {
|
|
76
|
+
aggregated.set(change.range.idString, change);
|
|
77
|
+
}
|
|
78
|
+
} else {
|
|
79
|
+
aggregated.set(change.range.idString, change);
|
|
80
|
+
}
|
|
81
|
+
},
|
|
82
|
+
delete: (key: string) => {
|
|
83
|
+
aggregated.delete(key);
|
|
84
|
+
},
|
|
85
|
+
size: () => aggregated.size,
|
|
86
|
+
value: aggregated,
|
|
87
|
+
};
|
|
88
|
+
},
|
|
89
|
+
delay,
|
|
90
|
+
);
|
|
91
|
+
};
|
|
92
|
+
|
|
36
93
|
export type ReplicationDomain<Args, T> = {
|
|
37
94
|
type: string;
|
|
38
95
|
fromEntry: ReplicationDomainMapper<T>;
|
|
@@ -40,6 +97,12 @@ export type ReplicationDomain<Args, T> = {
|
|
|
40
97
|
args: Args | undefined,
|
|
41
98
|
log: Log,
|
|
42
99
|
) => Promise<CoverRange> | CoverRange;
|
|
100
|
+
|
|
101
|
+
// to rebalance will return an async iterator of objects that will be added to the log
|
|
102
|
+
/* toRebalance(
|
|
103
|
+
change: ReplicationChange,
|
|
104
|
+
index: Index<EntryWithCoordinate>
|
|
105
|
+
): AsyncIterable<{ gid: string, entries: { coordinate: number, hash: string }[] }> | Promise<AsyncIterable<{ gid: string, entries: EntryWithCoordinate[] }>>; */
|
|
43
106
|
};
|
|
44
107
|
|
|
45
108
|
export const uniformToU32 = (cursor: number) => {
|
package/src/replication.ts
CHANGED
|
@@ -6,244 +6,18 @@ import {
|
|
|
6
6
|
variant,
|
|
7
7
|
vec,
|
|
8
8
|
} from "@dao-xyz/borsh";
|
|
9
|
-
import {
|
|
10
|
-
import { type Index
|
|
9
|
+
import { randomBytes } from "@peerbit/crypto";
|
|
10
|
+
import { type Index } from "@peerbit/indexer-interface";
|
|
11
11
|
import { TransportMessage } from "./message.js";
|
|
12
|
-
import {
|
|
12
|
+
import {
|
|
13
|
+
ReplicationIntent,
|
|
14
|
+
ReplicationRange,
|
|
15
|
+
type ReplicationRangeIndexable,
|
|
16
|
+
} from "./ranges.js";
|
|
17
|
+
import { Observer, Replicator, Role } from "./role.js";
|
|
13
18
|
|
|
14
19
|
export type ReplicationLimits = { min: MinReplicas; max?: MinReplicas };
|
|
15
20
|
|
|
16
|
-
export enum ReplicationIntent {
|
|
17
|
-
NonStrict = 0, // indicates that the segment will be replicated and nearby data might be replicated as well
|
|
18
|
-
Strict = 1, // only replicate data in the segment to the specified replicator, not any other data
|
|
19
|
-
}
|
|
20
|
-
|
|
21
|
-
export const getSegmentsFromOffsetAndRange = (
|
|
22
|
-
offset: number,
|
|
23
|
-
factor: number,
|
|
24
|
-
): [[number, number], [number, number]] => {
|
|
25
|
-
let start1 = offset;
|
|
26
|
-
let end1Unscaled = offset + factor; // only add factor if it is not 1 to prevent numerical issues (like (0.9 + 1) % 1 => 0.8999999)
|
|
27
|
-
let end1 = Math.min(end1Unscaled, MAX_U32);
|
|
28
|
-
return [
|
|
29
|
-
[start1, end1],
|
|
30
|
-
end1Unscaled > MAX_U32
|
|
31
|
-
? [0, (factor !== MAX_U32 ? offset + factor : offset) % MAX_U32]
|
|
32
|
-
: [start1, end1],
|
|
33
|
-
];
|
|
34
|
-
};
|
|
35
|
-
|
|
36
|
-
@variant(0)
|
|
37
|
-
export class ReplicationRange {
|
|
38
|
-
@field({ type: Uint8Array })
|
|
39
|
-
private id: Uint8Array;
|
|
40
|
-
|
|
41
|
-
@field({ type: "u64" })
|
|
42
|
-
timestamp: bigint;
|
|
43
|
-
|
|
44
|
-
@field({ type: "u32" })
|
|
45
|
-
private _offset: number;
|
|
46
|
-
|
|
47
|
-
@field({ type: "u32" })
|
|
48
|
-
private _factor: number;
|
|
49
|
-
|
|
50
|
-
@field({ type: "u8" })
|
|
51
|
-
mode: ReplicationIntent;
|
|
52
|
-
|
|
53
|
-
constructor(properties: {
|
|
54
|
-
id: Uint8Array;
|
|
55
|
-
offset: number;
|
|
56
|
-
factor: number;
|
|
57
|
-
timestamp: bigint;
|
|
58
|
-
mode: ReplicationIntent;
|
|
59
|
-
}) {
|
|
60
|
-
const { id, offset, factor, timestamp, mode } = properties;
|
|
61
|
-
this.id = id;
|
|
62
|
-
this._offset = offset;
|
|
63
|
-
this._factor = factor;
|
|
64
|
-
this.timestamp = timestamp;
|
|
65
|
-
this.mode = mode;
|
|
66
|
-
}
|
|
67
|
-
|
|
68
|
-
get factor(): number {
|
|
69
|
-
return this._factor;
|
|
70
|
-
}
|
|
71
|
-
|
|
72
|
-
get offset(): number {
|
|
73
|
-
return this._offset;
|
|
74
|
-
}
|
|
75
|
-
|
|
76
|
-
toReplicationRangeIndexable(key: PublicSignKey): ReplicationRangeIndexable {
|
|
77
|
-
return new ReplicationRangeIndexable({
|
|
78
|
-
id: this.id,
|
|
79
|
-
publicKeyHash: key.hashcode(),
|
|
80
|
-
offset: this.offset,
|
|
81
|
-
length: this.factor,
|
|
82
|
-
timestamp: this.timestamp,
|
|
83
|
-
mode: this.mode,
|
|
84
|
-
});
|
|
85
|
-
}
|
|
86
|
-
}
|
|
87
|
-
|
|
88
|
-
export class ReplicationRangeIndexable {
|
|
89
|
-
@id({ type: Uint8Array })
|
|
90
|
-
id: Uint8Array;
|
|
91
|
-
|
|
92
|
-
@field({ type: "string" })
|
|
93
|
-
hash: string;
|
|
94
|
-
|
|
95
|
-
@field({ type: "u64" })
|
|
96
|
-
timestamp: bigint;
|
|
97
|
-
|
|
98
|
-
@field({ type: "u32" })
|
|
99
|
-
start1: number;
|
|
100
|
-
|
|
101
|
-
@field({ type: "u32" })
|
|
102
|
-
end1: number;
|
|
103
|
-
|
|
104
|
-
@field({ type: "u32" })
|
|
105
|
-
start2: number;
|
|
106
|
-
|
|
107
|
-
@field({ type: "u32" })
|
|
108
|
-
end2: number;
|
|
109
|
-
|
|
110
|
-
@field({ type: "u32" })
|
|
111
|
-
width: number;
|
|
112
|
-
|
|
113
|
-
@field({ type: "u8" })
|
|
114
|
-
mode: ReplicationIntent;
|
|
115
|
-
|
|
116
|
-
constructor(
|
|
117
|
-
properties: {
|
|
118
|
-
id?: Uint8Array;
|
|
119
|
-
normalized?: boolean;
|
|
120
|
-
offset: number;
|
|
121
|
-
length: number;
|
|
122
|
-
mode?: ReplicationIntent;
|
|
123
|
-
timestamp?: bigint;
|
|
124
|
-
} & ({ publicKeyHash: string } | { publicKey: PublicSignKey }),
|
|
125
|
-
) {
|
|
126
|
-
this.id = properties.id ?? randomBytes(32);
|
|
127
|
-
this.hash =
|
|
128
|
-
(properties as { publicKeyHash: string }).publicKeyHash ||
|
|
129
|
-
(properties as { publicKey: PublicSignKey }).publicKey.hashcode();
|
|
130
|
-
if (!properties.normalized) {
|
|
131
|
-
this.transform({ length: properties.length, offset: properties.offset });
|
|
132
|
-
} else {
|
|
133
|
-
this.transform({
|
|
134
|
-
length: scaleToU32(properties.length),
|
|
135
|
-
offset: scaleToU32(properties.offset),
|
|
136
|
-
});
|
|
137
|
-
}
|
|
138
|
-
|
|
139
|
-
this.mode = properties.mode ?? ReplicationIntent.NonStrict;
|
|
140
|
-
this.timestamp = properties.timestamp || BigInt(0);
|
|
141
|
-
}
|
|
142
|
-
|
|
143
|
-
private transform(properties: { offset: number; length: number }) {
|
|
144
|
-
const ranges = getSegmentsFromOffsetAndRange(
|
|
145
|
-
properties.offset,
|
|
146
|
-
properties.length,
|
|
147
|
-
);
|
|
148
|
-
this.start1 = Math.round(ranges[0][0]);
|
|
149
|
-
this.end1 = Math.round(ranges[0][1]);
|
|
150
|
-
this.start2 = Math.round(ranges[1][0]);
|
|
151
|
-
this.end2 = Math.round(ranges[1][1]);
|
|
152
|
-
|
|
153
|
-
this.width =
|
|
154
|
-
this.end1 -
|
|
155
|
-
this.start1 +
|
|
156
|
-
(this.end2 < this.end1 ? this.end2 - this.start2 : 0);
|
|
157
|
-
|
|
158
|
-
if (
|
|
159
|
-
this.start1 > 0xffffffff ||
|
|
160
|
-
this.end1 > 0xffffffff ||
|
|
161
|
-
this.start2 > 0xffffffff ||
|
|
162
|
-
this.end2 > 0xffffffff ||
|
|
163
|
-
this.width > 0xffffffff ||
|
|
164
|
-
this.width < 0
|
|
165
|
-
) {
|
|
166
|
-
throw new Error("Segment coordinate out of bounds");
|
|
167
|
-
}
|
|
168
|
-
}
|
|
169
|
-
|
|
170
|
-
contains(point: number) {
|
|
171
|
-
return (
|
|
172
|
-
(point >= this.start1 && point < this.end1) ||
|
|
173
|
-
(point >= this.start2 && point < this.end2)
|
|
174
|
-
);
|
|
175
|
-
}
|
|
176
|
-
|
|
177
|
-
overlaps(other: ReplicationRangeIndexable, checkOther = true): boolean {
|
|
178
|
-
if (
|
|
179
|
-
this.contains(other.start1) ||
|
|
180
|
-
this.contains(other.start2) ||
|
|
181
|
-
this.contains(other.end1) ||
|
|
182
|
-
this.contains(other.end2)
|
|
183
|
-
) {
|
|
184
|
-
return true;
|
|
185
|
-
}
|
|
186
|
-
|
|
187
|
-
if (checkOther) {
|
|
188
|
-
return other.overlaps(this, false);
|
|
189
|
-
}
|
|
190
|
-
return false;
|
|
191
|
-
}
|
|
192
|
-
toReplicationRange() {
|
|
193
|
-
return new ReplicationRange({
|
|
194
|
-
id: this.id,
|
|
195
|
-
offset: this.start1,
|
|
196
|
-
factor: this.width,
|
|
197
|
-
timestamp: this.timestamp,
|
|
198
|
-
mode: this.mode,
|
|
199
|
-
});
|
|
200
|
-
}
|
|
201
|
-
|
|
202
|
-
distanceTo(point: number) {
|
|
203
|
-
let wrappedPoint = MAX_U32 - point;
|
|
204
|
-
return Math.min(
|
|
205
|
-
Math.abs(this.start1 - point),
|
|
206
|
-
Math.abs(this.end2 - point),
|
|
207
|
-
Math.abs(this.start1 - wrappedPoint),
|
|
208
|
-
Math.abs(this.end2 - wrappedPoint),
|
|
209
|
-
);
|
|
210
|
-
}
|
|
211
|
-
get wrapped() {
|
|
212
|
-
return this.end2 < this.end1;
|
|
213
|
-
}
|
|
214
|
-
|
|
215
|
-
get widthNormalized() {
|
|
216
|
-
return this.width / MAX_U32;
|
|
217
|
-
}
|
|
218
|
-
|
|
219
|
-
equals(other: ReplicationRangeIndexable) {
|
|
220
|
-
if (
|
|
221
|
-
equals(this.id, other.id) &&
|
|
222
|
-
this.hash === other.hash &&
|
|
223
|
-
this.timestamp === other.timestamp &&
|
|
224
|
-
this.mode === other.mode &&
|
|
225
|
-
this.start1 === other.start1 &&
|
|
226
|
-
this.end1 === other.end1 &&
|
|
227
|
-
this.start2 === other.start2 &&
|
|
228
|
-
this.end2 === other.end2 &&
|
|
229
|
-
this.width === other.width
|
|
230
|
-
) {
|
|
231
|
-
return true;
|
|
232
|
-
}
|
|
233
|
-
|
|
234
|
-
return false;
|
|
235
|
-
}
|
|
236
|
-
|
|
237
|
-
toString() {
|
|
238
|
-
let roundToTwoDecimals = (num: number) => Math.round(num * 100) / 100;
|
|
239
|
-
|
|
240
|
-
if (Math.abs(this.start1 - this.start2) < 0.0001) {
|
|
241
|
-
return `([${roundToTwoDecimals(this.start1 / MAX_U32)}, ${roundToTwoDecimals(this.end1 / MAX_U32)}])`;
|
|
242
|
-
}
|
|
243
|
-
return `([${roundToTwoDecimals(this.start1 / MAX_U32)}, ${roundToTwoDecimals(this.end1 / MAX_U32)}] [${roundToTwoDecimals(this.start2 / MAX_U32)}, ${roundToTwoDecimals(this.end2 / MAX_U32)}])`;
|
|
244
|
-
}
|
|
245
|
-
}
|
|
246
|
-
|
|
247
21
|
interface SharedLog {
|
|
248
22
|
replicas: Partial<ReplicationLimits>;
|
|
249
23
|
replicationIndex: Index<ReplicationRangeIndexable> | undefined;
|
|
@@ -343,7 +117,7 @@ export class StoppedReplicating extends TransportMessage {
|
|
|
343
117
|
@variant(1)
|
|
344
118
|
export class RelativeMinReplicas extends MinReplicas {
|
|
345
119
|
_value: number; // (0, 1]
|
|
346
|
-
|
|
120
|
+
|
|
347
121
|
constructor(value: number) {
|
|
348
122
|
super();
|
|
349
123
|
this._value = value;
|
package/src/role.ts
CHANGED
|
@@ -6,6 +6,7 @@
|
|
|
6
6
|
import { field, variant, vec } from "@dao-xyz/borsh";
|
|
7
7
|
|
|
8
8
|
export const MAX_U32 = 4294967295;
|
|
9
|
+
export const HALF_MAX_U32 = 2147483647; // rounded down
|
|
9
10
|
export const scaleToU32 = (value: number) => Math.round(MAX_U32 * value);
|
|
10
11
|
|
|
11
12
|
export const overlaps = (x1: number, x2: number, y1: number, y2: number) => {
|
package/src/utils.ts
ADDED
|
@@ -0,0 +1,49 @@
|
|
|
1
|
+
import { Entry, ShallowEntry } from "@peerbit/log";
|
|
2
|
+
import type { EntryWithRefs } from "./exchange-heads.js";
|
|
3
|
+
import { EntryReplicated } from "./ranges.js";
|
|
4
|
+
|
|
5
|
+
export const groupByGid = async <
|
|
6
|
+
T extends ShallowEntry | Entry<any> | EntryWithRefs<any> | EntryReplicated,
|
|
7
|
+
>(
|
|
8
|
+
entries: T[],
|
|
9
|
+
): Promise<Map<string, T[]>> => {
|
|
10
|
+
const groupByGid: Map<string, T[]> = new Map();
|
|
11
|
+
for (const head of entries) {
|
|
12
|
+
const gid =
|
|
13
|
+
head instanceof Entry
|
|
14
|
+
? (await head.getMeta()).gid
|
|
15
|
+
: head instanceof ShallowEntry
|
|
16
|
+
? head.meta.gid
|
|
17
|
+
: head instanceof EntryReplicated
|
|
18
|
+
? head.gid
|
|
19
|
+
: (await head.entry.getMeta()).gid;
|
|
20
|
+
let value = groupByGid.get(gid);
|
|
21
|
+
if (!value) {
|
|
22
|
+
value = [];
|
|
23
|
+
groupByGid.set(gid, value);
|
|
24
|
+
}
|
|
25
|
+
value.push(head);
|
|
26
|
+
}
|
|
27
|
+
return groupByGid;
|
|
28
|
+
};
|
|
29
|
+
|
|
30
|
+
export const groupByGidSync = async <T extends ShallowEntry | EntryReplicated>(
|
|
31
|
+
entries: T[],
|
|
32
|
+
): Promise<Map<string, T[]>> => {
|
|
33
|
+
const groupByGid: Map<string, T[]> = new Map();
|
|
34
|
+
for (const head of entries) {
|
|
35
|
+
const gid =
|
|
36
|
+
head instanceof Entry
|
|
37
|
+
? (await head.getMeta()).gid
|
|
38
|
+
: head instanceof ShallowEntry
|
|
39
|
+
? head.meta.gid
|
|
40
|
+
: head.gid;
|
|
41
|
+
let value = groupByGid.get(gid);
|
|
42
|
+
if (!value) {
|
|
43
|
+
value = [];
|
|
44
|
+
groupByGid.set(gid, value);
|
|
45
|
+
}
|
|
46
|
+
value.push(head);
|
|
47
|
+
}
|
|
48
|
+
return groupByGid;
|
|
49
|
+
};
|