@@ -166,6 +166,10 @@ class Own(ValType):
166
166
class Borrow (ValType ):
167
167
rt : ResourceType
168
168
169
+ @dataclass
170
+ class Stream (ValType ):
171
+ t : ValType
172
+
169
173
### Despecialization
170
174
171
175
def despecialize (t ):
@@ -193,6 +197,7 @@ def alignment(t):
193
197
case Variant (cases ) : return alignment_variant (cases )
194
198
case Flags (labels ) : return alignment_flags (labels )
195
199
case Own (_) | Borrow (_) : return 4
200
+ case Stream (_) : return 4
196
201
197
202
def alignment_record (fields ):
198
203
a = 1
@@ -243,6 +248,7 @@ def elem_size(t):
243
248
case Variant (cases ) : return elem_size_variant (cases )
244
249
case Flags (labels ) : return elem_size_flags (labels )
245
250
case Own (_) | Borrow (_) : return 4
251
+ case Stream (_) : return 4
246
252
247
253
def elem_size_record (fields ):
248
254
s = 0
@@ -303,6 +309,8 @@ class ComponentInstance:
303
309
active_sync_task : bool
304
310
pending_sync_tasks : list [asyncio .Future ]
305
311
async_subtasks : Table [AsyncSubtask ]
312
+ readable_streams : Table [ReadableStreamElem ]
313
+ writable_streams : Table [WritableStreamElem ]
306
314
307
315
def __init__ (self ):
308
316
self .may_leave = True
@@ -314,6 +322,8 @@ def __init__(self):
314
322
self .active_sync_task = False
315
323
self .pending_sync_tasks = []
316
324
self .async_subtasks = Table [AsyncSubtask ]()
325
+ self .readable_streams = Table [ReadableStreamElem ]()
326
+ self .writable_streams = Table [WritableStreamElem ]()
317
327
318
328
class HandleTables :
319
329
rt_to_table : MutableMapping [ResourceType , Table [HandleElem ]]
@@ -388,6 +398,28 @@ def __init__(self, rep, own, scope = None):
388
398
self .scope = scope
389
399
self .lend_count = 0
390
400
401
+ class Buffer :
402
+ cx : CallContext
403
+ ptr : int
404
+ length : int
405
+
406
+ def __init__ (self , cx , ptr , length ):
407
+ self .cx = cx
408
+ self .ptr = ptr
409
+ self .length = length
410
+
411
+ class StreamElem :
412
+ t : ValType
413
+ active : bool
414
+ writer : Optional [Buffer ]
415
+ reader : Optional [Buffer ]
416
+
417
+ def __init__ (self , t ):
418
+ self .t = t
419
+ self .active = True
420
+ self .writer = None
421
+ self .reader = None
422
+
391
423
class AsyncCallState (IntEnum ):
392
424
STARTING = 0
393
425
STARTED = 1
@@ -400,6 +432,10 @@ class EventCode(IntEnum):
400
432
CALL_RETURNED = AsyncCallState .RETURNED
401
433
CALL_DONE = AsyncCallState .DONE
402
434
YIELDED = 4
435
+ STREAM_READ = 5
436
+ STREAM_CLOSED = 6
437
+ STREAM_WROTE = 7
438
+ STREAM_CANCELLED = 8
403
439
404
440
current_task = asyncio .Lock ()
405
441
@@ -489,6 +525,12 @@ def async_subtask_made_progress(self, subtask):
489
525
subtask .enqueued = True
490
526
self .events .put_nowait (subtask )
491
527
528
+ def stream_new (self , t ):
529
+ s = StreamElem (t )
530
+ rsi = self .inst .readable_streams .add (s )
531
+ wsi = self .inst .writable_streams .add (s )
532
+ return (rsi , wsi )
533
+
492
534
def create_borrow (self ):
493
535
self .borrow_count += 1
494
536
@@ -624,6 +666,7 @@ def load(cx, ptr, t):
624
666
case Flags (labels ) : return load_flags (cx , ptr , labels )
625
667
case Own () : return lift_own (cx , load_int (cx , ptr , 4 ), t )
626
668
case Borrow () : return lift_borrow (cx , load_int (cx , ptr , 4 ), t )
669
+ case Stream () : return lift_stream (cx , load_int (cx , ptr , 4 ), t )
627
670
628
671
def load_int (cx , ptr , nbytes , signed = False ):
629
672
return int .from_bytes (cx .opts .memory [ptr : ptr + nbytes ], 'little' , signed = signed )
@@ -704,11 +747,14 @@ def load_string_from_range(cx, ptr, tagged_code_units):
704
747
def load_list (cx , ptr , elem_type ):
705
748
begin = load_int (cx , ptr , 4 )
706
749
length = load_int (cx , ptr + 4 , 4 )
707
- return load_list_from_range (cx , begin , length , elem_type )
750
+ return lift_list (cx , begin , length , elem_type )
708
751
709
- def load_list_from_range (cx , ptr , length , elem_type ):
752
+ def lift_list (cx , ptr , length , elem_type ):
710
753
trap_if (ptr != align_to (ptr , alignment (elem_type )))
711
754
trap_if (ptr + length * elem_size (elem_type ) > len (cx .opts .memory ))
755
+ return lift_list_after_checks (cx , ptr , length , elem_type )
756
+
757
+ def lift_list_after_checks (cx , ptr , length , elem_type ):
712
758
a = []
713
759
for i in range (length ):
714
760
a .append (load (cx , ptr + i * elem_size (elem_type ), elem_type ))
@@ -772,6 +818,10 @@ def lift_borrow(cx, i, t):
772
818
cx .track_owning_lend (h )
773
819
return h .rep
774
820
821
+ def lift_stream (cx , i , t ):
822
+ # TODO
823
+ pass
824
+
775
825
### Storing
776
826
777
827
def store (cx , v , t , ptr ):
@@ -797,6 +847,7 @@ def store(cx, v, t, ptr):
797
847
case Flags (labels ) : store_flags (cx , v , ptr , labels )
798
848
case Own () : store_int (cx , lower_own (cx .opts , v , t ), ptr , 4 )
799
849
case Borrow () : store_int (cx , lower_borrow (cx .opts , v , t ), ptr , 4 )
850
+ case Stream () : store_int (cx , lower_stream (cx .opts , v , t ), ptr , 4 )
800
851
801
852
def store_int (cx , v , ptr , nbytes , signed = False ):
802
853
cx .opts .memory [ptr : ptr + nbytes ] = int .to_bytes (v , nbytes , 'little' , signed = signed )
@@ -989,19 +1040,22 @@ def store_probably_utf16_to_latin1_or_utf16(cx, src, src_code_units):
989
1040
return (ptr , latin1_size )
990
1041
991
1042
def store_list (cx , v , ptr , elem_type ):
992
- begin , length = store_list_into_range (cx , v , elem_type )
1043
+ begin , length = lower_list (cx , v , elem_type )
993
1044
store_int (cx , begin , ptr , 4 )
994
1045
store_int (cx , length , ptr + 4 , 4 )
995
1046
996
- def store_list_into_range (cx , v , elem_type ):
1047
+ def lower_list (cx , v , elem_type ):
997
1048
byte_length = len (v ) * elem_size (elem_type )
998
1049
trap_if (byte_length >= (1 << 32 ))
999
1050
ptr = cx .opts .realloc (0 , 0 , alignment (elem_type ), byte_length )
1000
1051
trap_if (ptr != align_to (ptr , alignment (elem_type )))
1001
1052
trap_if (ptr + byte_length > len (cx .opts .memory ))
1053
+ lower_list_after_checks (cx , v , ptr , elem_type )
1054
+ return (ptr , len (v ))
1055
+
1056
+ def lower_list_after_checks (cx , v , ptr , elem_type ):
1002
1057
for i ,e in enumerate (v ):
1003
1058
store (cx , e , elem_type , ptr + i * elem_size (elem_type ))
1004
- return (ptr , len (v ))
1005
1059
1006
1060
def store_record (cx , v , ptr , fields ):
1007
1061
for f in fields :
@@ -1052,6 +1106,10 @@ def lower_borrow(cx, rep, t):
1052
1106
cx .create_borrow ()
1053
1107
return cx .inst .handles .add (t .rt , h )
1054
1108
1109
+ def lower_stream (cx , rep , t ):
1110
+ # TODO
1111
+ pass
1112
+
1055
1113
### Flattening
1056
1114
1057
1115
MAX_FLAT_PARAMS = 16
@@ -1101,6 +1159,7 @@ def flatten_type(t):
1101
1159
case Variant (cases ) : return flatten_variant (cases )
1102
1160
case Flags (labels ) : return ['i32' ]
1103
1161
case Own (_) | Borrow (_) : return ['i32' ]
1162
+ case Stream (_) : return ['i32' ]
1104
1163
1105
1164
def flatten_record (fields ):
1106
1165
flat = []
@@ -1162,6 +1221,7 @@ def lift_flat(cx, vi, t):
1162
1221
case Flags (labels ) : return lift_flat_flags (vi , labels )
1163
1222
case Own () : return lift_own (cx , vi .next ('i32' ), t )
1164
1223
case Borrow () : return lift_borrow (cx , vi .next ('i32' ), t )
1224
+ case Stream () : return lift_stream (cx , vi .next ('i32' ), t )
1165
1225
1166
1226
def lift_flat_unsigned (vi , core_width , t_width ):
1167
1227
i = vi .next ('i' + str (core_width ))
@@ -1184,7 +1244,7 @@ def lift_flat_string(cx, vi):
1184
1244
def lift_flat_list (cx , vi , elem_type ):
1185
1245
ptr = vi .next ('i32' )
1186
1246
length = vi .next ('i32' )
1187
- return load_list_from_range (cx , ptr , length , elem_type )
1247
+ return lift_list (cx , ptr , length , elem_type )
1188
1248
1189
1249
def lift_flat_record (cx , vi , fields ):
1190
1250
record = {}
@@ -1248,6 +1308,7 @@ def lower_flat(cx, v, t):
1248
1308
case Flags (labels ) : return lower_flat_flags (v , labels )
1249
1309
case Own () : return [lower_own (cx , v , t )]
1250
1310
case Borrow () : return [lower_borrow (cx , v , t )]
1311
+ case Stream () : return [lower_stream (cx , v , t )]
1251
1312
1252
1313
def lower_flat_signed (i , core_bits ):
1253
1314
if i < 0 :
@@ -1259,7 +1320,7 @@ def lower_flat_string(cx, v):
1259
1320
return [ptr , packed_length ]
1260
1321
1261
1322
def lower_flat_list (cx , v , elem_type ):
1262
- (ptr , length ) = store_list_into_range (cx , v , elem_type )
1323
+ (ptr , length ) = lower_list (cx , v , elem_type )
1263
1324
return [ptr , length ]
1264
1325
1265
1326
def lower_flat_record (cx , v , fields ):
@@ -1523,3 +1584,73 @@ async def canon_task_yield(task):
1523
1584
trap_if (task .opts .callback is not None )
1524
1585
await task .yield_ ()
1525
1586
return []
1587
+
1588
+ ### 🔀 `canon stream.new`
1589
+
1590
+ async def canon_stream_new (t , task , ptr ):
1591
+ rsi , wsi = task .stream_new (t )
1592
+ store (task , wsi , U32 (), ptr )
1593
+ return [rsi ]
1594
+
1595
+ ### 🔀 `canon stream.write`
1596
+
1597
+
1598
+ # TODO: do we want read/write to both support full/partial?
1599
+ async def canon_stream_write (t , sync , task , wsi , ptr , length , partial ):
1600
+ trap_if (length == 0 or length >= 2 ** 31 )
1601
+ trap_if (ptr != align_to (ptr , alignment (t )))
1602
+ trap_if (ptr + length > len (task .opts .memory ))
1603
+ s = self .inst .writable_streams .get (wsi )
1604
+ trap_if (s .t != t )
1605
+ trap_if (s .writer is not None )
1606
+ s .writer = Buffer (task , ptr , length )
1607
+ while s .writer :
1608
+ if not s .active :
1609
+ return pack_final_result (length - s .writer .length )
1610
+ if not s .reader :
1611
+ # TODO: this is all wrong
1612
+ if not sync :
1613
+ return [length - s .writer .length ]
1614
+ task .inst .calling_sync_import = True
1615
+ await ... TODO
1616
+ task .inst .calling_sync_import = False
1617
+ copy_writer_to_reader (s )
1618
+ return [length ]
1619
+
1620
+ def pack_final_result (nwritten ):
1621
+ assert (nwritten < 2 ** 31 )
1622
+ return [ (1 << 31 ) | nwritten ]
1623
+
1624
+ def copy_writer_to_reader (writer , reader ):
1625
+ copy_length = min (writer .length , reader .length )
1626
+ v = lift_list_after_checks (writer .cx , writer .ptr , copy_length , t )
1627
+ lower_list_after_checks (reader .cx , v , reader .ptr , t )
1628
+ writer .ptr += copy_length * elem_size (t )
1629
+ reader .ptr += copy_length * elem_size (t )
1630
+ writer .length -= copy_length
1631
+ reader .length -= copy_length
1632
+ if writer .length == 0 :
1633
+ TODO
1634
+ if reader .length == 0 :
1635
+ TODO
1636
+
1637
+ ### 🔀 `canon stream.close`
1638
+
1639
+ async def canon_stream_close (task , wsi ):
1640
+ # TODO
1641
+ pass
1642
+
1643
+ ### 🔀 `canon stream.read`
1644
+
1645
+ async def canon_stream_read (sync , task , rsi , ptr , length , partial ):
1646
+ trap_if (read_length == 0 )
1647
+ trap_if (ptr != align_to (ptr , alignment (t )))
1648
+ trap_if (ptr + read_length > len (task .opts .memory ))
1649
+ # TODO
1650
+ pass
1651
+
1652
+ ### 🔀 `canon stream.drop`
1653
+
1654
+ async def canon_stream_drop (task , rsi ):
1655
+ # TODO
1656
+ pass
0 commit comments