weida 0.1.0a16__py3-none-win_amd64.whl
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.
- weida/__init__.py +349 -0
- weida/_blob.py +246 -0
- weida/_cdef.h +2440 -0
- weida/_crypto.py +67 -0
- weida/_enums.py +209 -0
- weida/_errors.py +221 -0
- weida/_ffi.py +10 -0
- weida/_native.py +24 -0
- weida/_resolver.py +137 -0
- weida/_stats.py +217 -0
- weida/_sync.py +3101 -0
- weida/_values.py +1368 -0
- weida/_wait.py +148 -0
- weida/asyncio.py +1110 -0
- weida/weida.dll +0 -0
- weida-0.1.0a16.dist-info/METADATA +89 -0
- weida-0.1.0a16.dist-info/RECORD +18 -0
- weida-0.1.0a16.dist-info/WHEEL +4 -0
weida/__init__.py
ADDED
|
@@ -0,0 +1,349 @@
|
|
|
1
|
+
"""weida for Python, over the C ABI (`libweida`) with cffi.
|
|
2
|
+
|
|
3
|
+
The API mirrors the C ABI (docs/c/README.md): one class per handle, one
|
|
4
|
+
method per function. This module is the synchronous surface;
|
|
5
|
+
`weida.asyncio` has the same classes with coroutines for every method that
|
|
6
|
+
waits, and both share the value classes defined here.
|
|
7
|
+
"""
|
|
8
|
+
|
|
9
|
+
from weida._enums import (
|
|
10
|
+
Acknowledgement,
|
|
11
|
+
Alpn,
|
|
12
|
+
Ask,
|
|
13
|
+
Backpressure,
|
|
14
|
+
Congestion,
|
|
15
|
+
Deduplication,
|
|
16
|
+
DeliveryLevel,
|
|
17
|
+
Discovery,
|
|
18
|
+
Durability,
|
|
19
|
+
ErrorCode,
|
|
20
|
+
GiveUp,
|
|
21
|
+
IdentityEventKind,
|
|
22
|
+
IncomingKind,
|
|
23
|
+
LossCause,
|
|
24
|
+
OrderingMode,
|
|
25
|
+
OutboxFull,
|
|
26
|
+
PeerEventKind,
|
|
27
|
+
ProducerNaming,
|
|
28
|
+
ReceivedKind,
|
|
29
|
+
ReportMode,
|
|
30
|
+
)
|
|
31
|
+
from weida._blob import Bits, Blob, PieceList, TreeHasher, blob_id_of
|
|
32
|
+
from weida._errors import (
|
|
33
|
+
AlreadyRegistered,
|
|
34
|
+
Again,
|
|
35
|
+
Canceled,
|
|
36
|
+
Closed,
|
|
37
|
+
ConnectionLost,
|
|
38
|
+
DatagramsUnavailable,
|
|
39
|
+
Expired,
|
|
40
|
+
Indeterminate,
|
|
41
|
+
InvalidAddress,
|
|
42
|
+
InvalidArgument,
|
|
43
|
+
InvalidEndpointPath,
|
|
44
|
+
InvalidFingerprint,
|
|
45
|
+
Interrupted,
|
|
46
|
+
Io,
|
|
47
|
+
LimitExceeded,
|
|
48
|
+
Negotiation,
|
|
49
|
+
NoParkedConnection,
|
|
50
|
+
NoReply,
|
|
51
|
+
NotConnected,
|
|
52
|
+
Panic,
|
|
53
|
+
Protocol,
|
|
54
|
+
Rejected,
|
|
55
|
+
RuntimeFailure,
|
|
56
|
+
Tls,
|
|
57
|
+
TooLarge,
|
|
58
|
+
Transport,
|
|
59
|
+
UnknownEndpoint,
|
|
60
|
+
Unsupported,
|
|
61
|
+
Untrusted,
|
|
62
|
+
WeidaError,
|
|
63
|
+
)
|
|
64
|
+
from weida._errors import check as _check
|
|
65
|
+
from weida._native import ffi as _ffi
|
|
66
|
+
from weida._native import lib as _lib
|
|
67
|
+
from weida._resolver import Lookup
|
|
68
|
+
from weida._stats import (
|
|
69
|
+
ConnectionStats,
|
|
70
|
+
PathStats,
|
|
71
|
+
PeerEvent,
|
|
72
|
+
RemoteStats,
|
|
73
|
+
TransportStats,
|
|
74
|
+
UdpCounts,
|
|
75
|
+
)
|
|
76
|
+
from weida._sync import (
|
|
77
|
+
Acceptor,
|
|
78
|
+
Binding,
|
|
79
|
+
BusMember,
|
|
80
|
+
Consumer,
|
|
81
|
+
Cursors,
|
|
82
|
+
Delivery,
|
|
83
|
+
Dish,
|
|
84
|
+
FanOut,
|
|
85
|
+
Fetch,
|
|
86
|
+
Flow,
|
|
87
|
+
Handoff,
|
|
88
|
+
IdentityEvents,
|
|
89
|
+
Incoming,
|
|
90
|
+
IncomingFlow,
|
|
91
|
+
IncomingRequest,
|
|
92
|
+
IncomingTransfer,
|
|
93
|
+
Listener,
|
|
94
|
+
LocalBinding,
|
|
95
|
+
Order,
|
|
96
|
+
OutgoingTransfer,
|
|
97
|
+
Paired,
|
|
98
|
+
Peer,
|
|
99
|
+
PeerEvents,
|
|
100
|
+
Piece,
|
|
101
|
+
PipeBinding,
|
|
102
|
+
Publisher,
|
|
103
|
+
Puller,
|
|
104
|
+
Pusher,
|
|
105
|
+
Radio,
|
|
106
|
+
Received,
|
|
107
|
+
Relay,
|
|
108
|
+
RelayRequest,
|
|
109
|
+
Replier,
|
|
110
|
+
ReplyStream,
|
|
111
|
+
Reporter,
|
|
112
|
+
Requester,
|
|
113
|
+
Respondent,
|
|
114
|
+
Runtime,
|
|
115
|
+
SeedSession,
|
|
116
|
+
Seeder,
|
|
117
|
+
Segment,
|
|
118
|
+
Subscriber,
|
|
119
|
+
SurveyRun,
|
|
120
|
+
Surveyor,
|
|
121
|
+
Swarm,
|
|
122
|
+
UnixBinding,
|
|
123
|
+
)
|
|
124
|
+
from weida._values import (
|
|
125
|
+
ANY_KEY,
|
|
126
|
+
ClientTls,
|
|
127
|
+
CursorEntry,
|
|
128
|
+
CursorSet,
|
|
129
|
+
DishDrops,
|
|
130
|
+
Drained,
|
|
131
|
+
FlowMeta,
|
|
132
|
+
FlowStats,
|
|
133
|
+
Gap,
|
|
134
|
+
GuaranteeSet,
|
|
135
|
+
Identity,
|
|
136
|
+
IdentityEvent,
|
|
137
|
+
IdentitySource,
|
|
138
|
+
IncomingFlowStats,
|
|
139
|
+
IncomingMeta,
|
|
140
|
+
Limits,
|
|
141
|
+
LocalPrincipal,
|
|
142
|
+
Message,
|
|
143
|
+
ReconnectPolicy,
|
|
144
|
+
RuntimeConfig,
|
|
145
|
+
ServerTls,
|
|
146
|
+
TopicDrops,
|
|
147
|
+
TransferMeta,
|
|
148
|
+
Trust,
|
|
149
|
+
TrustSource,
|
|
150
|
+
WeakIdentitySource,
|
|
151
|
+
WeakTrustSource,
|
|
152
|
+
WindowsPrincipal,
|
|
153
|
+
)
|
|
154
|
+
|
|
155
|
+
|
|
156
|
+
def version() -> str:
|
|
157
|
+
"""The version of the loaded `libweida`."""
|
|
158
|
+
return _ffi.string(_lib.weida_version()).decode()
|
|
159
|
+
|
|
160
|
+
|
|
161
|
+
def strerror(code: int) -> str:
|
|
162
|
+
"""The C ABI's text for a status code, positive or negative; "unknown
|
|
163
|
+
error" for any other."""
|
|
164
|
+
return _ffi.string(_lib.weida_strerror(code)).decode()
|
|
165
|
+
|
|
166
|
+
|
|
167
|
+
def new_trace() -> str:
|
|
168
|
+
"""A fresh sampled W3C traceparent for `TransferMeta(trace=...)`; weida
|
|
169
|
+
never starts a trace on its own."""
|
|
170
|
+
buffer = _ffi.new("char[]", _lib.WEIDA_TRACEPARENT_LEN + 1)
|
|
171
|
+
_check(_lib.weida_new_trace(buffer, len(buffer)))
|
|
172
|
+
return _ffi.string(buffer).decode()
|
|
173
|
+
|
|
174
|
+
|
|
175
|
+
# The `Limits.datagram_receive_bytes` a profile with flows uses.
|
|
176
|
+
DEFAULT_DATAGRAM_RECEIVE_BYTES: int = _lib.WEIDA_DEFAULT_DATAGRAM_RECEIVE_BYTES
|
|
177
|
+
|
|
178
|
+
# How many layers a segment carries: 0 to MAX_SEGMENT_LAYERS - 1.
|
|
179
|
+
MAX_SEGMENT_LAYERS: int = _lib.WEIDA_MAX_SEGMENT_LAYERS
|
|
180
|
+
|
|
181
|
+
# The first cursor level an application may name; the levels below it are
|
|
182
|
+
# weida's own, the `Acknowledgement` members.
|
|
183
|
+
CURSOR_LEVEL_APPLICATION_FLOOR: int = _lib.WEIDA_CURSOR_LEVEL_APPLICATION_FLOOR
|
|
184
|
+
|
|
185
|
+
# The bytes of a blob's root, its BLAKE3 hash.
|
|
186
|
+
BLOB_ID_LEN: int = _lib.WEIDA_BLOB_ID_LEN
|
|
187
|
+
|
|
188
|
+
# The most pieces a blob may have.
|
|
189
|
+
MAX_PIECES: int = _lib.WEIDA_MAX_PIECES
|
|
190
|
+
|
|
191
|
+
# The smallest, the largest and the default piece size, as a power of two.
|
|
192
|
+
PIECE_SIZE_MIN_LOG2: int = _lib.WEIDA_PIECE_SIZE_MIN_LOG2
|
|
193
|
+
PIECE_SIZE_MAX_LOG2: int = _lib.WEIDA_PIECE_SIZE_MAX_LOG2
|
|
194
|
+
PIECE_SIZE_DEFAULT_LOG2: int = _lib.WEIDA_PIECE_SIZE_DEFAULT_LOG2
|
|
195
|
+
|
|
196
|
+
|
|
197
|
+
__all__ = [
|
|
198
|
+
# Values.
|
|
199
|
+
"ANY_KEY",
|
|
200
|
+
"Bits",
|
|
201
|
+
"Blob",
|
|
202
|
+
"ClientTls",
|
|
203
|
+
"ConnectionStats",
|
|
204
|
+
"CursorEntry",
|
|
205
|
+
"CursorSet",
|
|
206
|
+
"DishDrops",
|
|
207
|
+
"Drained",
|
|
208
|
+
"FlowMeta",
|
|
209
|
+
"FlowStats",
|
|
210
|
+
"Gap",
|
|
211
|
+
"GuaranteeSet",
|
|
212
|
+
"Identity",
|
|
213
|
+
"IdentityEvent",
|
|
214
|
+
"IdentitySource",
|
|
215
|
+
"Incoming",
|
|
216
|
+
"IncomingFlowStats",
|
|
217
|
+
"IncomingMeta",
|
|
218
|
+
"Limits",
|
|
219
|
+
"Lookup",
|
|
220
|
+
"LocalPrincipal",
|
|
221
|
+
"Message",
|
|
222
|
+
"Order",
|
|
223
|
+
"PathStats",
|
|
224
|
+
"PeerEvent",
|
|
225
|
+
"Piece",
|
|
226
|
+
"PieceList",
|
|
227
|
+
"Received",
|
|
228
|
+
"ReconnectPolicy",
|
|
229
|
+
"RemoteStats",
|
|
230
|
+
"RuntimeConfig",
|
|
231
|
+
"ServerTls",
|
|
232
|
+
"TopicDrops",
|
|
233
|
+
"TransferMeta",
|
|
234
|
+
"TransportStats",
|
|
235
|
+
"Trust",
|
|
236
|
+
"TreeHasher",
|
|
237
|
+
"TrustSource",
|
|
238
|
+
"UdpCounts",
|
|
239
|
+
"WeakIdentitySource",
|
|
240
|
+
"WeakTrustSource",
|
|
241
|
+
"WindowsPrincipal",
|
|
242
|
+
# Enumerations.
|
|
243
|
+
"Acknowledgement",
|
|
244
|
+
"Alpn",
|
|
245
|
+
"Ask",
|
|
246
|
+
"Backpressure",
|
|
247
|
+
"Congestion",
|
|
248
|
+
"Deduplication",
|
|
249
|
+
"DeliveryLevel",
|
|
250
|
+
"Discovery",
|
|
251
|
+
"Durability",
|
|
252
|
+
"ErrorCode",
|
|
253
|
+
"GiveUp",
|
|
254
|
+
"IdentityEventKind",
|
|
255
|
+
"IncomingKind",
|
|
256
|
+
"LossCause",
|
|
257
|
+
"OrderingMode",
|
|
258
|
+
"OutboxFull",
|
|
259
|
+
"PeerEventKind",
|
|
260
|
+
"ProducerNaming",
|
|
261
|
+
"ReceivedKind",
|
|
262
|
+
"ReportMode",
|
|
263
|
+
"BLOB_ID_LEN",
|
|
264
|
+
"CURSOR_LEVEL_APPLICATION_FLOOR",
|
|
265
|
+
"DEFAULT_DATAGRAM_RECEIVE_BYTES",
|
|
266
|
+
"MAX_PIECES",
|
|
267
|
+
"MAX_SEGMENT_LAYERS",
|
|
268
|
+
"PIECE_SIZE_DEFAULT_LOG2",
|
|
269
|
+
"PIECE_SIZE_MAX_LOG2",
|
|
270
|
+
"PIECE_SIZE_MIN_LOG2",
|
|
271
|
+
# Handles.
|
|
272
|
+
"Acceptor",
|
|
273
|
+
"Binding",
|
|
274
|
+
"BusMember",
|
|
275
|
+
"Consumer",
|
|
276
|
+
"Cursors",
|
|
277
|
+
"Delivery",
|
|
278
|
+
"Dish",
|
|
279
|
+
"FanOut",
|
|
280
|
+
"Fetch",
|
|
281
|
+
"Flow",
|
|
282
|
+
"Handoff",
|
|
283
|
+
"IdentityEvents",
|
|
284
|
+
"IncomingFlow",
|
|
285
|
+
"IncomingRequest",
|
|
286
|
+
"IncomingTransfer",
|
|
287
|
+
"Listener",
|
|
288
|
+
"LocalBinding",
|
|
289
|
+
"OutgoingTransfer",
|
|
290
|
+
"Paired",
|
|
291
|
+
"Peer",
|
|
292
|
+
"PeerEvents",
|
|
293
|
+
"PipeBinding",
|
|
294
|
+
"Publisher",
|
|
295
|
+
"Puller",
|
|
296
|
+
"Pusher",
|
|
297
|
+
"Radio",
|
|
298
|
+
"Relay",
|
|
299
|
+
"RelayRequest",
|
|
300
|
+
"Replier",
|
|
301
|
+
"ReplyStream",
|
|
302
|
+
"Reporter",
|
|
303
|
+
"Requester",
|
|
304
|
+
"Respondent",
|
|
305
|
+
"Runtime",
|
|
306
|
+
"SeedSession",
|
|
307
|
+
"Seeder",
|
|
308
|
+
"Segment",
|
|
309
|
+
"Subscriber",
|
|
310
|
+
"SurveyRun",
|
|
311
|
+
"Surveyor",
|
|
312
|
+
"Swarm",
|
|
313
|
+
"UnixBinding",
|
|
314
|
+
# Errors.
|
|
315
|
+
"WeidaError",
|
|
316
|
+
"AlreadyRegistered",
|
|
317
|
+
"Again",
|
|
318
|
+
"Canceled",
|
|
319
|
+
"Closed",
|
|
320
|
+
"ConnectionLost",
|
|
321
|
+
"DatagramsUnavailable",
|
|
322
|
+
"Expired",
|
|
323
|
+
"Indeterminate",
|
|
324
|
+
"InvalidAddress",
|
|
325
|
+
"InvalidArgument",
|
|
326
|
+
"InvalidEndpointPath",
|
|
327
|
+
"InvalidFingerprint",
|
|
328
|
+
"Interrupted",
|
|
329
|
+
"Io",
|
|
330
|
+
"LimitExceeded",
|
|
331
|
+
"Negotiation",
|
|
332
|
+
"NoParkedConnection",
|
|
333
|
+
"NoReply",
|
|
334
|
+
"NotConnected",
|
|
335
|
+
"Panic",
|
|
336
|
+
"Protocol",
|
|
337
|
+
"Rejected",
|
|
338
|
+
"RuntimeFailure",
|
|
339
|
+
"Tls",
|
|
340
|
+
"TooLarge",
|
|
341
|
+
"Transport",
|
|
342
|
+
"UnknownEndpoint",
|
|
343
|
+
"Unsupported",
|
|
344
|
+
"Untrusted",
|
|
345
|
+
"blob_id_of",
|
|
346
|
+
"new_trace",
|
|
347
|
+
"strerror",
|
|
348
|
+
"version",
|
|
349
|
+
]
|
weida/_blob.py
ADDED
|
@@ -0,0 +1,246 @@
|
|
|
1
|
+
"""The piece tree of SWARM (docs/c/swarm.md): a blob's root and pieces, its
|
|
2
|
+
piece list, the tree hasher and piece maps, handles both surfaces share, and
|
|
3
|
+
the fetch options."""
|
|
4
|
+
|
|
5
|
+
from typing import Optional, Tuple
|
|
6
|
+
|
|
7
|
+
from weida._errors import check
|
|
8
|
+
from weida._native import ffi, lib
|
|
9
|
+
from weida._values import _count, _Fields, _Handle, _Made, _millis, _of
|
|
10
|
+
|
|
11
|
+
_U32_MAX = 2**32 - 1
|
|
12
|
+
|
|
13
|
+
|
|
14
|
+
def _bounded(name: str, value, maximum: int) -> int:
|
|
15
|
+
"""`_count`, and at most `maximum`: the C parameter's range."""
|
|
16
|
+
value = _count(name, value)
|
|
17
|
+
if value > maximum:
|
|
18
|
+
raise ValueError(f"{name} must be at most {maximum}")
|
|
19
|
+
return value
|
|
20
|
+
|
|
21
|
+
|
|
22
|
+
def _bytes_arg(value):
|
|
23
|
+
"""Any bytes-like as the `uint8_t` array C reads; cffi raises
|
|
24
|
+
`TypeError` for anything else."""
|
|
25
|
+
return ffi.from_buffer("uint8_t[]", value)
|
|
26
|
+
|
|
27
|
+
|
|
28
|
+
def blob_id_of(data) -> bytes:
|
|
29
|
+
"""The id of `data` (any bytes-like): its BLAKE3 hash, `BLOB_ID_LEN` bytes."""
|
|
30
|
+
src = _bytes_arg(data)
|
|
31
|
+
out = ffi.new("uint8_t[]", lib.WEIDA_BLOB_ID_LEN)
|
|
32
|
+
check(lib.weida_blob_id_of(src, len(src), out))
|
|
33
|
+
return ffi.buffer(out)[:]
|
|
34
|
+
|
|
35
|
+
|
|
36
|
+
class Blob(_Handle):
|
|
37
|
+
"""A blob of `size` bytes whose hash is `root`, cut into pieces of
|
|
38
|
+
`2**piece_log2` bytes."""
|
|
39
|
+
|
|
40
|
+
def __init__(self, root, size: int, piece_log2: int = lib.WEIDA_PIECE_SIZE_DEFAULT_LOG2) -> None:
|
|
41
|
+
"""`root` is any bytes-like of `BLOB_ID_LEN` bytes, else a
|
|
42
|
+
`ValueError`. C refuses, with `InvalidArgument`, a `piece_log2`
|
|
43
|
+
outside 10..24, more than `MAX_PIECES` pieces, or an empty blob whose
|
|
44
|
+
root is not the hash of no bytes."""
|
|
45
|
+
r = _bytes_arg(root)
|
|
46
|
+
if len(r) != lib.WEIDA_BLOB_ID_LEN:
|
|
47
|
+
raise ValueError(f"root is {lib.WEIDA_BLOB_ID_LEN} bytes")
|
|
48
|
+
out = ffi.new("weida_blob **")
|
|
49
|
+
check(
|
|
50
|
+
lib.weida_blob_new(
|
|
51
|
+
r,
|
|
52
|
+
_bounded("size", size, 2**64 - 1),
|
|
53
|
+
_bounded("piece_log2", piece_log2, 255),
|
|
54
|
+
out,
|
|
55
|
+
)
|
|
56
|
+
)
|
|
57
|
+
super().__init__(out[0], lib.weida_blob_drop)
|
|
58
|
+
|
|
59
|
+
@classmethod
|
|
60
|
+
def _wrap(cls, ptr) -> "Blob":
|
|
61
|
+
blob = cls.__new__(cls)
|
|
62
|
+
_Handle.__init__(blob, ptr, lib.weida_blob_drop)
|
|
63
|
+
return blob
|
|
64
|
+
|
|
65
|
+
def root(self) -> bytes:
|
|
66
|
+
"""The blob's id, its BLAKE3 hash: `BLOB_ID_LEN` bytes."""
|
|
67
|
+
out = ffi.new("uint8_t[]", lib.WEIDA_BLOB_ID_LEN)
|
|
68
|
+
check(lib.weida_blob_root(self._p(), out))
|
|
69
|
+
return ffi.buffer(out)[:]
|
|
70
|
+
|
|
71
|
+
def size(self) -> int:
|
|
72
|
+
"""The blob's length in bytes."""
|
|
73
|
+
return lib.weida_blob_size(self._p())
|
|
74
|
+
|
|
75
|
+
def piece_log2(self) -> int:
|
|
76
|
+
"""The piece size as a power of two."""
|
|
77
|
+
return lib.weida_blob_piece_log2(self._p())
|
|
78
|
+
|
|
79
|
+
def count(self) -> int:
|
|
80
|
+
"""How many pieces the blob has."""
|
|
81
|
+
return lib.weida_blob_count(self._p())
|
|
82
|
+
|
|
83
|
+
def span(self, index: int) -> Optional[Tuple[int, int]]:
|
|
84
|
+
"""The bytes piece `index` covers, `(start, end)` with `end`
|
|
85
|
+
exclusive; `None` past the last piece."""
|
|
86
|
+
start, end = ffi.new("uint64_t *"), ffi.new("uint64_t *")
|
|
87
|
+
if check(lib.weida_blob_span(self._p(), _bounded("index", index, _U32_MAX), start, end)) == 1:
|
|
88
|
+
return (start[0], end[0])
|
|
89
|
+
return None
|
|
90
|
+
|
|
91
|
+
|
|
92
|
+
class PieceList(_Made):
|
|
93
|
+
"""A blob's piece list: one chaining value per piece, which checks each
|
|
94
|
+
piece against the root on its own."""
|
|
95
|
+
|
|
96
|
+
_DROP = lib.weida_piece_list_drop
|
|
97
|
+
_MADE_BY = "PieceList.of, PieceList.parse, TreeHasher.finish or Fetch.list"
|
|
98
|
+
|
|
99
|
+
@classmethod
|
|
100
|
+
def of(cls, data, piece_log2: int = lib.WEIDA_PIECE_SIZE_DEFAULT_LOG2) -> "PieceList":
|
|
101
|
+
"""The piece list of `data` (any bytes-like) in one call."""
|
|
102
|
+
src = _bytes_arg(data)
|
|
103
|
+
out = ffi.new("weida_piece_list **")
|
|
104
|
+
check(lib.weida_piece_list_of(src, len(src), _bounded("piece_log2", piece_log2, 255), out))
|
|
105
|
+
return cls._wrap(out[0])
|
|
106
|
+
|
|
107
|
+
@classmethod
|
|
108
|
+
def parse(cls, blob: Blob, data) -> "PieceList":
|
|
109
|
+
"""The piece list of `blob` from its bytes (`to_bytes()`); raises
|
|
110
|
+
`InvalidArgument` unless they are one chaining value per piece
|
|
111
|
+
hashing to the root."""
|
|
112
|
+
src = _bytes_arg(data)
|
|
113
|
+
out = ffi.new("weida_piece_list **")
|
|
114
|
+
check(lib.weida_piece_list_parse(_of("blob", blob, Blob), src, len(src), out))
|
|
115
|
+
return cls._wrap(out[0])
|
|
116
|
+
|
|
117
|
+
def blob(self) -> Blob:
|
|
118
|
+
"""The blob this list belongs to."""
|
|
119
|
+
out = ffi.new("weida_blob **")
|
|
120
|
+
check(lib.weida_piece_list_blob(self._p(), out))
|
|
121
|
+
return Blob._wrap(out[0])
|
|
122
|
+
|
|
123
|
+
def to_bytes(self) -> bytes:
|
|
124
|
+
"""The list's bytes, a copy; empty for a blob of at most one piece."""
|
|
125
|
+
length = ffi.new("size_t *")
|
|
126
|
+
ptr = lib.weida_piece_list_to_bytes(self._p(), length)
|
|
127
|
+
if length[0] == 0:
|
|
128
|
+
return b""
|
|
129
|
+
return ffi.buffer(ptr, length[0])[:]
|
|
130
|
+
|
|
131
|
+
def check(self, index: int, piece) -> bool:
|
|
132
|
+
"""Whether `piece` (any bytes-like) is piece `index` of the blob."""
|
|
133
|
+
src = _bytes_arg(piece)
|
|
134
|
+
return check(lib.weida_piece_list_check(self._p(), _bounded("index", index, _U32_MAX), src, len(src))) == 1
|
|
135
|
+
|
|
136
|
+
|
|
137
|
+
class TreeHasher(_Handle):
|
|
138
|
+
"""Hashes a blob fed in parts into its `PieceList`."""
|
|
139
|
+
|
|
140
|
+
def __init__(self, piece_log2: int = lib.WEIDA_PIECE_SIZE_DEFAULT_LOG2) -> None:
|
|
141
|
+
"""A `piece_log2` outside 10..24 raises `InvalidArgument`."""
|
|
142
|
+
out = ffi.new("weida_tree_hasher **")
|
|
143
|
+
check(lib.weida_tree_hasher_new(_bounded("piece_log2", piece_log2, 255), out))
|
|
144
|
+
super().__init__(out[0], lib.weida_tree_hasher_drop)
|
|
145
|
+
|
|
146
|
+
def update(self, data) -> "TreeHasher":
|
|
147
|
+
"""Feeds the next bytes (any bytes-like); returns this hasher."""
|
|
148
|
+
src = _bytes_arg(data)
|
|
149
|
+
check(lib.weida_tree_hasher_update(self._p(), src, len(src)))
|
|
150
|
+
return self
|
|
151
|
+
|
|
152
|
+
def finish(self) -> PieceList:
|
|
153
|
+
"""The piece list of every byte fed. Consumes the hasher, also on
|
|
154
|
+
failure: more than `MAX_PIECES` pieces is `InvalidArgument`."""
|
|
155
|
+
out = ffi.new("weida_piece_list **")
|
|
156
|
+
check(lib.weida_tree_hasher_finish(self._consume(), out))
|
|
157
|
+
return PieceList._wrap(out[0])
|
|
158
|
+
|
|
159
|
+
|
|
160
|
+
class Bits(_Handle):
|
|
161
|
+
"""A piece map: one bit per piece, all clear when made with `Bits(length)`."""
|
|
162
|
+
|
|
163
|
+
def __init__(self, length: int) -> None:
|
|
164
|
+
out = ffi.new("weida_bits **")
|
|
165
|
+
check(lib.weida_bits_new(_bounded("length", length, _U32_MAX), out))
|
|
166
|
+
super().__init__(out[0], lib.weida_bits_drop)
|
|
167
|
+
|
|
168
|
+
@classmethod
|
|
169
|
+
def _wrap(cls, ptr) -> "Bits":
|
|
170
|
+
bits = cls.__new__(cls)
|
|
171
|
+
_Handle.__init__(bits, ptr, lib.weida_bits_drop)
|
|
172
|
+
return bits
|
|
173
|
+
|
|
174
|
+
@classmethod
|
|
175
|
+
def full(cls, length: int) -> "Bits":
|
|
176
|
+
"""A map of `length` bits, all set."""
|
|
177
|
+
out = ffi.new("weida_bits **")
|
|
178
|
+
check(lib.weida_bits_full(_bounded("length", length, _U32_MAX), out))
|
|
179
|
+
return cls._wrap(out[0])
|
|
180
|
+
|
|
181
|
+
@classmethod
|
|
182
|
+
def from_bytes(cls, length: int, data) -> "Bits":
|
|
183
|
+
"""A map of `length` bits from `as_bytes()`'s form; `InvalidArgument`
|
|
184
|
+
unless `data` is `(length + 7) // 8` bytes with clear padding bits."""
|
|
185
|
+
n = _bounded("length", length, _U32_MAX)
|
|
186
|
+
src = _bytes_arg(data)
|
|
187
|
+
out = ffi.new("weida_bits **")
|
|
188
|
+
check(lib.weida_bits_from_bytes(n, src, len(src), out))
|
|
189
|
+
return cls._wrap(out[0])
|
|
190
|
+
|
|
191
|
+
def as_bytes(self) -> bytes:
|
|
192
|
+
"""The map's bytes, a copy: bit `i` is bit `i % 8`, least significant
|
|
193
|
+
first, of byte `i // 8`."""
|
|
194
|
+
p = self._p()
|
|
195
|
+
n = (lib.weida_bits_len(p) + 7) // 8
|
|
196
|
+
buf = ffi.new("uint8_t[]", max(n, 1))
|
|
197
|
+
out_n = ffi.new("size_t *")
|
|
198
|
+
check(lib.weida_bits_as_bytes(p, buf, n, out_n))
|
|
199
|
+
return ffi.buffer(buf, out_n[0])[:]
|
|
200
|
+
|
|
201
|
+
def len(self) -> int:
|
|
202
|
+
"""How many bits the map has."""
|
|
203
|
+
return lib.weida_bits_len(self._p())
|
|
204
|
+
|
|
205
|
+
def ones(self) -> int:
|
|
206
|
+
"""How many bits are set."""
|
|
207
|
+
return lib.weida_bits_ones(self._p())
|
|
208
|
+
|
|
209
|
+
def get(self, index: int) -> bool:
|
|
210
|
+
"""Whether bit `index` is set; `False` past the end."""
|
|
211
|
+
return lib.weida_bits_get(self._p(), _bounded("index", index, _U32_MAX)) == 1
|
|
212
|
+
|
|
213
|
+
def all(self) -> bool:
|
|
214
|
+
"""Whether every bit is set."""
|
|
215
|
+
return lib.weida_bits_all(self._p()) == 1
|
|
216
|
+
|
|
217
|
+
def set(self, index: int, on: bool) -> None:
|
|
218
|
+
"""Sets or clears bit `index`; past the end it does nothing."""
|
|
219
|
+
check(lib.weida_bits_set(self._p(), _bounded("index", index, _U32_MAX), 1 if on else 0))
|
|
220
|
+
|
|
221
|
+
|
|
222
|
+
class _FetchOptions(_Fields):
|
|
223
|
+
"""`FetchOptions`, the keyword arguments of `Swarm.fetch`: `list` a
|
|
224
|
+
`PieceList`, `stall` and `have_refresh` seconds, the rest `int`s."""
|
|
225
|
+
|
|
226
|
+
_OUT = "weida_fetch_options **"
|
|
227
|
+
_NEW = lib.weida_fetch_options_new
|
|
228
|
+
_DROP = lib.weida_fetch_options_drop
|
|
229
|
+
_FIELDS = (
|
|
230
|
+
("stall", lib.weida_fetch_options_set_stall_ms, _millis),
|
|
231
|
+
("min_rate", lib.weida_fetch_options_set_min_rate, _count),
|
|
232
|
+
("per_peer", lib.weida_fetch_options_set_per_peer, _count),
|
|
233
|
+
("max_inflight", lib.weida_fetch_options_set_max_inflight, _count),
|
|
234
|
+
("max_peers", lib.weida_fetch_options_set_max_peers, _count),
|
|
235
|
+
("have_refresh", lib.weida_fetch_options_set_have_refresh_ms, _millis),
|
|
236
|
+
)
|
|
237
|
+
|
|
238
|
+
def _known(self) -> set:
|
|
239
|
+
return super()._known() | {"list"}
|
|
240
|
+
|
|
241
|
+
def _extra(self, handle) -> None:
|
|
242
|
+
if "list" in self._fields:
|
|
243
|
+
check(lib.weida_fetch_options_set_list(handle, _of("list", self._fields["list"], PieceList)))
|
|
244
|
+
|
|
245
|
+
|
|
246
|
+
__all__ = ["Bits", "Blob", "PieceList", "TreeHasher", "blob_id_of"]
|