Movatterモバイル変換


[0]ホーム

URL:


Skip to content

Navigation Menu

Sign in
Appearance settings

Search code, repositories, users, issues, pull requests...

Provide feedback

We read every piece of feedback, and take your input very seriously.

Saved searches

Use saved searches to filter your results more quickly

Sign up
Appearance settings

Commit7319295

Browse files
committed
Add infrastructure to lazily parse CXXRTL packets.
At the moment this still forces each packet to a deserializedrepresentation, but it opens the door to avoiding this for packetsgoing through secondary links.
1 parentfce5805 commit7319295

File tree

3 files changed

+111
-57
lines changed

3 files changed

+111
-57
lines changed

‎src/cxxrtl/client.ts‎

Lines changed: 33 additions & 22 deletions
Original file line numberDiff line numberDiff line change
@@ -16,35 +16,39 @@ export enum ConnectionState {
1616
// Note that we trust that server returns well-formed JSON. It would take far too much time to
1717
// verify its adherence to the schema here, for little gain.
1818
exportclassConnection{
19+
privatereadonlylink:link.ILink;
20+
1921
private_state=ConnectionState.Initializing;
2022

2123
private_commands:string[]=[];
2224
private_events:string[]=[];
2325
private_itemValuesEncodings:string[]=[];
2426

2527
privatepromises:{
26-
resolve:(response:proto.AnyResponse)=>void;
28+
resolve:(response:link.Packet<proto.AnyResponse>)=>void;
2729
reject:(error:Error)=>void;
2830
}[]=[];
2931
privatetimestamps:Date[]=[];
3032

3133
privatesendIndex:number=0;
3234
privaterecvIndex:number=0;
3335

34-
constructor(privatereadonlylink:link.ILink){
36+
constructor(link_:link.ILink){
37+
this.link=link_;
3538
this.link.onRecv=this.onLinkRecv.bind(this);
3639
this.link.onDone=this.onLinkDone.bind(this);
37-
this.send({
40+
this.send(link.Packet.fromObject({
3841
type:'greeting',
3942
version:0,
40-
});
43+
}));
4144
}
4245

4346
dispose():void{
4447
this.link.dispose();
4548
}
4649

47-
privatetraceSend(packet:proto.ClientPacket){
50+
privatetraceSend(linkPacket:link.Packet<proto.ClientPacket>){
51+
constpacket=linkPacket.asObject();
4852
if(packet.type==='greeting'){
4953
console.debug('[CXXRTL] C>S',packet);
5054
}elseif(packet.type==='command'){
@@ -53,7 +57,8 @@ export class Connection {
5357
}
5458
}
5559

56-
privatetraceRecv(packet:proto.ServerPacket){
60+
privatetraceRecv(linkPacket:link.Packet<proto.ServerPacket>){
61+
constpacket=linkPacket.asObject();
5762
if(packet.type==='greeting'){
5863
console.debug('[CXXRTL] S>C',packet);
5964
}elseif(packet.type==='response'){
@@ -67,17 +72,18 @@ export class Connection {
6772
}
6873
}
6974

70-
privateasyncsend(packet:proto.ClientPacket):Promise<void>{
71-
this.traceSend(packet);
75+
privateasyncsend(linkPacket:link.Packet<proto.ClientPacket>):Promise<void>{
76+
this.traceSend(linkPacket);
7277
if(this._state===ConnectionState.Disconnected){
7378
thrownewError('unable to send packet after link is shutdown');
7479
}else{
75-
this.link.send(packet);
80+
this.link.send(linkPacket);
7681
}
7782
}
7883

79-
privateasynconLinkRecv(packet:proto.ServerPacket):Promise<void>{
80-
this.traceRecv(packet);
84+
privateasynconLinkRecv(linkPacket:link.Packet<proto.ServerPacket>):Promise<void>{
85+
this.traceRecv(linkPacket);
86+
constpacket=linkPacket.asObject();
8187
if(this._state===ConnectionState.Initializing&&packet.type==='greeting'){
8288
if(packet.version===0){
8389
this._commands=packet.commands;
@@ -93,15 +99,15 @@ export class Connection {
9399
constnextPromise=this.promises.shift();
94100
if(nextPromise!==undefined){
95101
if(packet.type==='response'){
96-
nextPromise.resolve(packet);
102+
nextPromise.resolve(link.Packet.fromObject(packet));
97103
}else{
98104
nextPromise.reject(newCommandError(packet));
99105
}
100106
}else{
101107
this.rejectPromises(newError(`unexpected '${packet.type}' reply with no commands queued`));
102108
}
103109
}elseif(this._state===ConnectionState.Connected&&packet.type==='event'){
104-
awaitthis.onEvent(packet);
110+
awaitthis.onEvent(link.Packet.fromObject(packet));
105111
}else{
106112
this.rejectPromises(newError(`unexpected${packet.type} packet received for${this._state} connection`));
107113
}
@@ -119,7 +125,7 @@ export class Connection {
119125
}
120126
}
121127

122-
asyncperform(command:proto.AnyCommand):Promise<proto.AnyResponse>{
128+
asyncexchange(command:link.Packet<proto.AnyCommand>):Promise<link.Packet<proto.AnyResponse>>{
123129
awaitthis.send(command);
124130
returnnewPromise((resolve,reject)=>{
125131
this.promises.push({ resolve, reject});
@@ -130,7 +136,7 @@ export class Connection {
130136

131137
asynconDisconnected():Promise<void>{}
132138

133-
asynconEvent(_event:proto.AnyEvent):Promise<void>{}
139+
asynconEvent(_event:link.Packet<proto.AnyEvent>):Promise<void>{}
134140

135141
getstate():ConnectionState{
136142
returnthis._state;
@@ -148,31 +154,36 @@ export class Connection {
148154
returnthis._itemValuesEncodings.slice();
149155
}
150156

157+
privateasynccommand<Textendsproto.AnyResponse>(command:proto.AnyCommand):Promise<T>{
158+
constresponse=awaitthis.exchange(link.Packet.fromObject(command));
159+
returnresponse.cast<T>().asObject();
160+
}
161+
151162
asynclistScopes(command:proto.CommandListScopes):Promise<proto.ResponseListScopes>{
152-
returnawaitthis.perform(command)asproto.ResponseListScopes;
163+
returnthis.command<proto.ResponseListScopes>(command);
153164
}
154165

155166
asynclistItems(command:proto.CommandListItems):Promise<proto.ResponseListItems>{
156-
returnawaitthis.perform(command)asproto.ResponseListItems;
167+
returnthis.command<proto.ResponseListItems>(command);
157168
}
158169

159170
asyncreferenceItems(command:proto.CommandReferenceItems):Promise<proto.ResponseReferenceItems>{
160-
returnawaitthis.perform(command)asproto.ResponseReferenceItems;
171+
returnthis.command<proto.ResponseReferenceItems>(command);
161172
}
162173

163174
asyncqueryInterval(command:proto.CommandQueryInterval):Promise<proto.ResponseQueryInterval>{
164-
returnawaitthis.perform(command)asproto.ResponseQueryInterval;
175+
returnthis.command<proto.ResponseQueryInterval>(command);
165176
}
166177

167178
asyncgetSimulationStatus(command:proto.CommandGetSimulationStatus):Promise<proto.ResponseGetSimulationStatus>{
168-
returnawaitthis.perform(command)asproto.ResponseGetSimulationStatus;
179+
returnthis.command<proto.ResponseGetSimulationStatus>(command);
169180
}
170181

171182
asyncrunSimulation(command:proto.CommandRunSimulation):Promise<proto.ResponseRunSimulation>{
172-
returnawaitthis.perform(command)asproto.ResponseRunSimulation;
183+
returnthis.command<proto.ResponseRunSimulation>(command);
173184
}
174185

175186
asyncpauseSimulation(command:proto.CommandPauseSimulation):Promise<proto.ResponsePauseSimulation>{
176-
returnawaitthis.perform(command)asproto.ResponsePauseSimulation;
187+
returnthis.command<proto.ResponsePauseSimulation>(command);
177188
}
178189
}

‎src/cxxrtl/link.ts‎

Lines changed: 59 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -2,13 +2,52 @@ import * as stream from 'node:stream';
22

33
import*asprotofrom'./proto';
44

5+
// Lazily serialize/deserialize packets in case they only need to be passed along.
6+
exportclassPacket<T>{
7+
privateconstructor(
8+
privateserialized:string|undefined,
9+
privatedeserialized:T|undefined,
10+
){}
11+
12+
staticfromString<T>(serialized:string){
13+
returnnewPacket<T>(serialized,undefined);
14+
}
15+
16+
staticfromObject<T>(deserialized:T){
17+
returnnewPacket<T>(undefined,deserialized);
18+
}
19+
20+
asString():string{
21+
if(this.serialized===undefined){
22+
this.serialized=JSON.stringify(this.deserialized!);
23+
}
24+
returnthis.serialized;
25+
}
26+
27+
asObject():T{
28+
if(this.deserialized===undefined){
29+
this.deserialized=<T>JSON.parse(this.serialized!);
30+
}
31+
returnthis.deserialized;
32+
}
33+
34+
cast<U>():Packet<U>{
35+
return<Packet<U>>(<unknown>(this));
36+
}
37+
38+
// Make sure we don't unintentionally negate the performance advantages of this wrapper.
39+
toJSON():never{
40+
thrownewError('call Packet.asObject() instead of serializing with JSON.stringify()');
41+
}
42+
}
43+
544
exportinterfaceILink{
645
dispose():void;
746

8-
onRecv:(packet:proto.ServerPacket)=>Promise<void>;
47+
onRecv:(packet:Packet<proto.ServerPacket>)=>Promise<void>;
948
onDone:()=>Promise<void>;
1049

11-
send(packet:proto.ClientPacket):Promise<void>;
50+
send(packet:Packet<proto.ClientPacket>):Promise<void>;
1251
}
1352

1453
exportclassMockLinkimplementsILink{
@@ -22,24 +61,24 @@ export class MockLink implements ILink {
2261
}
2362
}
2463

25-
asynconRecv(_serverPacket:proto.ServerPacket):Promise<void>{}
64+
asynconRecv(_serverPacket:Packet<proto.ServerPacket>):Promise<void>{}
2665

2766
asynconDone():Promise<void>{}
2867

29-
asyncsend(clientPacket:proto.ClientPacket):Promise<void>{
68+
asyncsend(clientPacket:Packet<proto.ClientPacket>):Promise<void>{
3069
if(this.conversation.length===0){
3170
thrownewError('premature end of conversation');
3271
}
3372

3473
const[[expectedClient,expectedServer], ...restOfConversation]=this.conversation;
3574

36-
if(JSON.stringify(clientPacket)===JSON.stringify(expectedClient)){
75+
if(clientPacket.asString()===JSON.stringify(expectedClient)){
3776
if(expectedServerinstanceofArray){
3877
for(constserverPacketofexpectedServer){
39-
awaitthis.onRecv(serverPacket);
78+
awaitthis.onRecv(Packet.fromObject(serverPacket));
4079
}
4180
}else{
42-
awaitthis.onRecv(expectedServer);
81+
awaitthis.onRecv(Packet.fromObject(expectedServer));
4382
}
4483
}else{
4584
console.error('unexpected client packet',clientPacket,'; expected:',expectedClient);
@@ -82,28 +121,28 @@ export class NodeStreamLink implements ILink {
82121
// Second, convert the packet text to JSON. This can throw errors e.g. if there is foreign
83122
// data injected between server replies, or the server is malfunctioning. In that case,
84123
// stop processing input.
85-
constpackets:proto.ServerPacket[]=[];
124+
constpackets:Packet<proto.ServerPacket>[]=[];
86125
for(constpacketTextofpacketTexts){
87-
try{
88-
packets.push(JSON.parse(packetText)asproto.ServerPacket);
89-
}catch(error){
90-
console.error('malformed JSON: ',packetText);
91-
this.stream.pause();
92-
return;
93-
}
126+
packets.push(Packet.fromString<proto.ServerPacket>(packetText));
94127
}
95128

96129
// Finally, run the handler for each of the packets. If the handler blocks, don't wait for
97130
// its completion, but run the next handler anyway; this is because a handler can send
98131
// another client packet, causing `onStreamData` to be re-entered, anyway.
99132
for(constpacketofpackets){
100-
(async(packet:proto.ServerPacket)=>{
133+
constsuccess=(async(packet)=>{
101134
try{
102135
awaitthis.onRecv(packet);
136+
returntrue;
103137
}catch(error){
104138
console.error('uncaught error in onRecv',error);
139+
this.stream.pause();
140+
returnfalse;
105141
}
106142
})(packet);
143+
if(!success){
144+
break;
145+
}
107146
}
108147
}
109148

@@ -119,11 +158,12 @@ export class NodeStreamLink implements ILink {
119158
this.stream.destroy();
120159
}
121160

122-
asynconRecv(_serverPacket:proto.ServerPacket):Promise<void>{}
161+
asynconRecv(_serverPacket:Packet<proto.ServerPacket>):Promise<void>{}
123162

124163
asynconDone():Promise<void>{}
125164

126-
asyncsend(clientPacket:proto.ClientPacket):Promise<void>{
127-
this.stream.write(JSON.stringify(clientPacket)+'\0');
165+
asyncsend(clientPacket:Packet<proto.ClientPacket>):Promise<void>{
166+
this.stream.write(clientPacket.asString());
167+
this.stream.write('\0');
128168
}
129169
}

‎src/debug/session.ts‎

Lines changed: 19 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
import*asvscodefrom'vscode';
22

33
import*asprotofrom'../cxxrtl/proto';
4-
import{ILink}from'../cxxrtl/link';
4+
import*aslinkfrom'../cxxrtl/link';
55
import{Connection}from'../cxxrtl/client';
66
import{TimeInterval,TimePoint}from'../model/time';
77
import{Diagnostic,DiagnosticType,Reference,Sample,UnboundReference}from'../model/sample';
@@ -40,10 +40,10 @@ export enum SimulationPauseReason {
4040
exportclassSession{
4141
privateconnection:Connection;
4242

43-
privatesecondaryLinks:ILink[]=[];
43+
privatesecondaryLinks:link.ILink[]=[];
4444
privategreetingPacketPromise:Promise<proto.ServerGreeting>;
4545

46-
constructor(link:ILink){
46+
constructor(link:link.ILink){
4747
this.connection=newConnection(link);
4848
this.greetingPacketPromise=newPromise((resolve,_reject)=>{
4949
this.connection.onConnected=async(greetingPacket)=>resolve(greetingPacket);
@@ -53,10 +53,11 @@ export class Session {
5353
secondaryLink.onDone();
5454
}
5555
};
56-
this.connection.onEvent=async(event)=>{
56+
this.connection.onEvent=async(linkEvent)=>{
5757
for(constsecondaryLinkofthis.secondaryLinks){
58-
secondaryLink.onRecv(event);
58+
secondaryLink.onRecv(linkEvent);
5959
}
60+
constevent=linkEvent.asObject();
6061
if(event.event==='simulation_paused'){
6162
awaitthis.handleSimulationPausedEvent(event.cause);
6263
}elseif(event.event==='simulation_finished'){
@@ -70,33 +71,35 @@ export class Session {
7071
this.connection.dispose();
7172
}
7273

73-
createSecondaryLink():ILink{
74-
constlink:ILink={
74+
createSecondaryLink():link.ILink{
75+
constsecondaryLink:link.ILink={
7576
dispose:()=>{
76-
this.secondaryLinks.splice(this.secondaryLinks.indexOf(link));
77+
this.secondaryLinks.splice(this.secondaryLinks.indexOf(secondaryLink));
7778
},
7879

79-
send:async(clientPacket)=>{
80-
if(clientPacket.type==='greeting'){
80+
send:async(linkCommandPacket)=>{
81+
constpacket=linkCommandPacket.asObject();
82+
if(packet.type==='greeting'){
8183
constserverGreetingPacket=awaitthis.greetingPacketPromise;
82-
if(clientPacket.version===serverGreetingPacket.version){
83-
awaitlink.onRecv(serverGreetingPacket);
84+
if(packet.version===serverGreetingPacket.version){
85+
awaitsecondaryLink.onRecv(link.Packet.fromObject(serverGreetingPacket));
8486
}else{
8587
thrownewError(
86-
`Secondary link requested greeting version${clientPacket.version}, `+
88+
`Secondary link requested greeting version${packet.version}, `+
8789
`but server greeting version is${serverGreetingPacket.version}`
8890
);
8991
}
9092
}else{
91-
constserverPacket=awaitthis.connection.perform(clientPacket);
92-
awaitlink.onRecv(serverPacket);
93+
constlinkResponsePacket=awaitthis.connection.exchange(
94+
linkCommandPacket.cast<proto.AnyCommand>());
95+
awaitsecondaryLink.onRecv(linkResponsePacket);
9396
}
9497
},
9598

9699
onRecv:async(serverPacket)=>{},
97100
onDone:async()=>{},
98101
};
99-
returnlink;
102+
returnsecondaryLink;
100103
}
101104

102105
// ======================================== Inspecting the design

0 commit comments

Comments
 (0)

[8]ページ先頭

©2009-2025 Movatter.jp