@ -103,22 +103,37 @@ pub const MessageTag = enum(u16) {
/ / next : 0x0b
/ / next : 0x0b
} ;
} ;
/ / / the return value type from ` read ` fn .
pub const ParsedMessage = struct {
value : Message ,
arena : ? * std . heap . ArenaAllocator = null , / / null for void message tags
/ / / releases all resources used by the message .
pub fn deinit ( self : @This ( ) ) void {
if ( self . arena ) | a | {
const allocator = a . child_allocator ;
a . deinit ( ) ;
allocator . destroy ( a ) ;
}
}
} ;
/ / / reads and parses a single message from the input stream reader .
/ / / reads and parses a single message from the input stream reader .
/ / / propagates reader errors as is . for example , a closed reader returns
/ / / propagates reader errors as is . for example , a closed reader returns
/ / / error . EndOfStream .
/ / / error . EndOfStream .
/ / /
/ / /
/ / / callers must deallocate resources with Message . free when done .
/ / / callers must deallocate resources with ParsedMessage. deinit when done .
pub fn read ( allocator : mem . Allocator , reader : anytype ) ! Message {
pub fn read ( allocator : mem . Allocator , reader : anytype ) ! Parsed Message {
/ / alternative is @intToEnum ( reader . ReadIntLittle ( u16 ) ) but it may panic .
/ / alternative is @intToEnum ( reader . ReadIntLittle ( u16 ) ) but it may panic .
const tag = try reader . readEnum ( MessageTag , . Little ) ;
const tag = try reader . readEnum ( MessageTag , . Little ) ;
const len = try reader . readIntLittle ( u64 ) ;
const len = try reader . readIntLittle ( u64 ) ;
if ( len = = 0 ) {
if ( len = = 0 ) {
return switch ( tag ) {
return switch ( tag ) {
. ping = > Message { . ping = { } } ,
. ping = > . { . value = . { . ping = { } } } ,
. pong = > Message { . pong = { } } ,
. pong = > . { . value = . { . pong = { } } } ,
. poweroff = > Message { . poweroff = { } } ,
. poweroff = > . { . value = . { . poweroff = { } } } ,
. standby = > Message { . standby = { } } ,
. standby = > . { . value = . { . standby = { } } } ,
. wakeup = > Message { . wakeup = { } } ,
. wakeup = > . { . value = . { . wakeup = { } } } ,
else = > Error . CommReadZeroLenInNonVoidTag ,
else = > Error . CommReadZeroLenInNonVoidTag ,
} ;
} ;
}
}
@ -126,24 +141,22 @@ pub fn read(allocator: mem.Allocator, reader: anytype) !Message {
var bytes = try allocator . alloc ( u8 , len ) ;
var bytes = try allocator . alloc ( u8 , len ) ;
defer allocator . free ( bytes ) ;
defer allocator . free ( bytes ) ;
try reader . readNoEof ( bytes ) ;
try reader . readNoEof ( bytes ) ;
const jopt = json . ParseOptions { . allocator = allocator , . ignore_unknown_fields = true } ;
var jstream = json . TokenStream . init ( bytes ) ;
return switch ( tag ) {
return switch ( tag ) {
. ping , . pong , . poweroff , . standby , . wakeup = > unreachable , / / handled above
. ping , . pong , . poweroff , . standby , . wakeup = > unreachable , / / handled above
. wifi_connect = > Message {
inline else = > | t | {
. wifi_connect = try json . parse ( Message . WifiConnect , & jstream , jopt ) ,
var arena = try allocator . create ( std . heap . ArenaAllocator ) ;
} ,
arena . * = std . heap . ArenaAllocator . init ( allocator ) ;
. network_report = > Message {
errdefer {
. network_report = try json . parse ( Message . NetworkReport , & jstream , jopt ) ,
arena . deinit ( ) ;
} ,
allocator . destroy ( arena ) ;
. get_network_report = > Message {
}
. get_network_report = try json . parse ( Message . GetNetworkReport , & jstream , jopt ) ,
const jopt = std . json . ParseOptions { . ignore_unknown_fields = true , . allocate = . alloc_always } ;
} ,
const v = try json . parseFromSliceLeaky ( std . meta . TagPayload ( Message , t ) , arena . allocator ( ) , bytes , jopt ) ;
. poweroff_progress = > Message{
const parsed = Parsed Message{
. poweroff_progress = try json . parse ( Message . PoweroffProgress , & jstream , jopt ) ,
. arena = arena ,
} ,
. value = @unionInit ( Message , @tagName ( t ) , v ) ,
. bitcoind_report = > Message {
} ;
. bitcoind_report = try json . parse ( Message . BitcoindReport , & jstream , jopt ) ,
return parsed ;
} ,
} ,
} ;
} ;
}
}
@ -151,35 +164,25 @@ pub fn read(allocator: mem.Allocator, reader: anytype) !Message {
/ / / outputs the message msg using writer .
/ / / outputs the message msg using writer .
/ / / all allocated resources are freed upon return .
/ / / all allocated resources are freed upon return .
pub fn write ( allocator : mem . Allocator , writer : anytype , msg : Message ) ! void {
pub fn write ( allocator : mem . Allocator , writer : anytype , msg : Message ) ! void {
const jopt = . { . whitespace = null } ;
var data = ByteArrayList . init ( allocator ) ;
var data = ByteArrayList . init ( allocator ) ;
defer data . deinit ( ) ;
defer data . deinit ( ) ;
switch ( msg ) {
switch ( msg ) {
. ping , . pong , . poweroff , . standby , . wakeup = > { } , / / zero length payload
. ping , . pong , . poweroff , . standby , . wakeup = > { } , / / zero length payload
. wifi_connect = > try json . stringify ( msg . wifi_connect , jopt , data . writer ( ) ) ,
. wifi_connect = > try json . stringify ( msg . wifi_connect , . { } , data . writer ( ) ) ,
. network_report = > try json . stringify ( msg . network_report , jopt , data . writer ( ) ) ,
. network_report = > try json . stringify ( msg . network_report , . { } , data . writer ( ) ) ,
. get_network_report = > try json . stringify ( msg . get_network_report , jopt , data . writer ( ) ) ,
. get_network_report = > try json . stringify ( msg . get_network_report , . { } , data . writer ( ) ) ,
. poweroff_progress = > try json . stringify ( msg . poweroff_progress , jopt , data . writer ( ) ) ,
. poweroff_progress = > try json . stringify ( msg . poweroff_progress , . { } , data . writer ( ) ) ,
. bitcoind_report = > try json . stringify ( msg . bitcoind_report , jopt , data . writer ( ) ) ,
. bitcoind_report = > try json . stringify ( msg . bitcoind_report , . { } , data . writer ( ) ) ,
}
}
if ( data . items . len > std . math . maxInt ( u64 ) ) {
if ( data . items . len > std . math . maxInt ( u64 ) ) {
return Error . CommWriteTooLarge ;
return Error . CommWriteTooLarge ;
}
}
try writer . writeIntLittle ( u16 , @ enumToInt ( msg ) ) ;
try writer . writeIntLittle ( u16 , @ intFromEnum ( msg ) ) ;
try writer . writeIntLittle ( u64 , data . items . len ) ;
try writer . writeIntLittle ( u64 , data . items . len ) ;
try writer . writeAll ( data . items ) ;
try writer . writeAll ( data . items ) ;
}
}
pub fn free ( allocator : mem . Allocator , m : Message ) void {
switch ( m ) {
. ping , . pong , . poweroff , . standby , . wakeup = > { } , / / zero length payload
else = > | v | {
json . parseFree ( @TypeOf ( v ) , v , . { . allocator = allocator } ) ;
} ,
}
}
/ / TODO : use fifo
/ / TODO : use fifo
/ /
/ /
/ / var buf = std . fifo . LinearFifo ( u8 , . Dynamic ) . init ( t . allocator ) ;
/ / var buf = std . fifo . LinearFifo ( u8 , . Dynamic ) . init ( t . allocator ) ;
@ -206,16 +209,16 @@ test "read" {
var buf = std . ArrayList ( u8 ) . init ( t . allocator ) ;
var buf = std . ArrayList ( u8 ) . init ( t . allocator ) ;
defer buf . deinit ( ) ;
defer buf . deinit ( ) ;
try buf . writer ( ) . writeIntLittle ( u16 , @ enumToInt ( msg ) ) ;
try buf . writer ( ) . writeIntLittle ( u16 , @ intFromEnum ( msg ) ) ;
try buf . writer ( ) . writeIntLittle ( u64 , data . items . len ) ;
try buf . writer ( ) . writeIntLittle ( u64 , data . items . len ) ;
try buf . writer ( ) . writeAll ( data . items ) ;
try buf . writer ( ) . writeAll ( data . items ) ;
var bs = std . io . fixedBufferStream ( buf . items ) ;
var bs = std . io . fixedBufferStream ( buf . items ) ;
const res = try read ( t . allocator , bs . reader ( ) ) ;
const res = try read ( t . allocator , bs . reader ( ) ) ;
defer free( t . allocator , res ) ;
defer res. deinit ( ) ;
try t . expectEqualStrings ( msg . wifi_connect . ssid , res . wifi_connect. ssid ) ;
try t . expectEqualStrings ( msg . wifi_connect . ssid , res . value. wifi_connect. ssid ) ;
try t . expectEqualStrings ( msg . wifi_connect . password , res . wifi_connect. password ) ;
try t . expectEqualStrings ( msg . wifi_connect . password , res . value. wifi_connect. password ) ;
}
}
test " write " {
test " write " {
@ -229,7 +232,7 @@ test "write" {
const payload = " { \" ssid \" : \" wlan \" , \" password \" : \" secret \" } " ;
const payload = " { \" ssid \" : \" wlan \" , \" password \" : \" secret \" } " ;
var js = std . ArrayList ( u8 ) . init ( t . allocator ) ;
var js = std . ArrayList ( u8 ) . init ( t . allocator ) ;
defer js . deinit ( ) ;
defer js . deinit ( ) ;
try js . writer ( ) . writeIntLittle ( u16 , @ enumToInt ( msg ) ) ;
try js . writer ( ) . writeIntLittle ( u16 , @ intFromEnum ( msg ) ) ;
try js . writer ( ) . writeIntLittle ( u64 , payload . len ) ;
try js . writer ( ) . writeIntLittle ( u64 , payload . len ) ;
try js . appendSlice ( payload ) ;
try js . appendSlice ( payload ) ;
@ -255,8 +258,8 @@ test "write/read void tags" {
try write ( t . allocator , buf . writer ( ) , m ) ;
try write ( t . allocator , buf . writer ( ) , m ) ;
var bs = std . io . fixedBufferStream ( buf . items ) ;
var bs = std . io . fixedBufferStream ( buf . items ) ;
const res = try read ( t . allocator , bs . reader ( ) ) ;
const res = try read ( t . allocator , bs . reader ( ) ) ;
free( t . allocator , res ) ; / / noop
res. deinit ( ) ; / / noop due to void type
try t . expectEqual ( m , res );
try t . expectEqual ( m , res .value );
}
}
}
}
@ -283,7 +286,7 @@ test "msg sequence" {
var bs = std . io . fixedBufferStream ( buf . items ) ;
var bs = std . io . fixedBufferStream ( buf . items ) ;
for ( msgs ) | m | {
for ( msgs ) | m | {
const res = try read ( t . allocator , bs . reader ( ) ) ;
const res = try read ( t . allocator , bs . reader ( ) ) ;
defer free( t . allocator , res ) ;
defer res. deinit ( ) ;
try t . expectEqual ( @as ( MessageTag , m ) , @as ( MessageTag , res )) ;
try t . expectEqual ( @as ( MessageTag , m ) , @as ( MessageTag , res .value )) ;
}
}
}
}