Browse Source
Format is "le16 len; u8 message[len]" same as wire format specified in BOLTs, even though the endian conversion is overkill for local messages. Signed-off-by: Rusty Russell <rusty@rustcorp.com.au>ppa-0.6.1
Rusty Russell
8 years ago
3 changed files with 164 additions and 1 deletions
@ -0,0 +1,129 @@ |
|||||
|
#include <ccan/endian/endian.h> |
||||
|
/* FIXME: io_plan needs size_t */ |
||||
|
#include <unistd.h> |
||||
|
#include <ccan/io/io_plan.h> |
||||
|
#include <ccan/short_types/short_types.h> |
||||
|
#include <wire/wire_io.h> |
||||
|
|
||||
|
/*
|
||||
|
* OK, this is a little tricky. ccan/io lets you create your own plans, |
||||
|
* beyond the standard io_read/io_write etc. It provides a union to place |
||||
|
* scratch data, and it's almost enough for our purposes. |
||||
|
*/ |
||||
|
|
||||
|
/* 2 bytes for the length header. */ |
||||
|
#define HEADER_LEN (sizeof(le16)) |
||||
|
|
||||
|
/* Since length can only be 64k, this is an impossible value. */ |
||||
|
#define INSIDE_HEADER_BIT 0x80000000 |
||||
|
|
||||
|
/* arg->u2.s contains length we've read, arg->u1.vp contains u8 **data. */ |
||||
|
static int do_read_wire_header(int fd, struct io_plan_arg *arg) |
||||
|
{ |
||||
|
ssize_t ret; |
||||
|
size_t len = arg->u2.s & ~INSIDE_HEADER_BIT; |
||||
|
u8 *p = *(u8 **)arg->u1.vp; |
||||
|
|
||||
|
ret = read(fd, p + len, HEADER_LEN - len); |
||||
|
if (ret <= 0) |
||||
|
return -1; |
||||
|
arg->u2.s += ret; |
||||
|
|
||||
|
/* Both bytes read? Set up for normal read of data. */ |
||||
|
if (arg->u2.s == INSIDE_HEADER_BIT + HEADER_LEN) { |
||||
|
arg->u2.s = be16_to_cpu(*(be16 *)p); |
||||
|
/* A type-only message is not unheard of, so optimize a little */ |
||||
|
if (arg->u2.s != HEADER_LEN) |
||||
|
tal_resize((u8 **)arg->u1.vp, arg->u2.s); |
||||
|
arg->u1.vp = *(u8 **)arg->u1.vp; |
||||
|
} |
||||
|
|
||||
|
return arg->u2.s == 0; |
||||
|
} |
||||
|
|
||||
|
static int do_read_wire(int fd, struct io_plan_arg *arg) |
||||
|
{ |
||||
|
ssize_t ret; |
||||
|
|
||||
|
/* Still reading header? */ |
||||
|
if (arg->u2.s & INSIDE_HEADER_BIT) |
||||
|
return do_read_wire_header(fd, arg); |
||||
|
|
||||
|
/* Normal read */ |
||||
|
ret = read(fd, arg->u1.cp, arg->u2.s); |
||||
|
if (ret <= 0) |
||||
|
return -1; |
||||
|
|
||||
|
arg->u1.cp += ret; |
||||
|
arg->u2.s -= ret; |
||||
|
return arg->u2.s == 0; |
||||
|
} |
||||
|
|
||||
|
struct io_plan *io_read_wire_(struct io_conn *conn, |
||||
|
const tal_t *ctx, |
||||
|
u8 **data, |
||||
|
struct io_plan *(*next)(struct io_conn *, void *), |
||||
|
void *next_arg) |
||||
|
{ |
||||
|
struct io_plan_arg *arg = io_plan_arg(conn, IO_IN); |
||||
|
|
||||
|
/* We allocate data now; saves storing ctx, and lets us read in len. */ |
||||
|
arg->u1.vp = data; |
||||
|
*data = tal_arr(ctx, u8, HEADER_LEN); |
||||
|
|
||||
|
/* We use u2 to store the length we've read. */ |
||||
|
arg->u2.s = INSIDE_HEADER_BIT; |
||||
|
return io_set_plan(conn, IO_IN, do_read_wire, next, next_arg); |
||||
|
} |
||||
|
|
||||
|
/* arg->u2.s contains length we've written, arg->u1 contains u8 *data. */ |
||||
|
static int do_write_wire_header(int fd, struct io_plan_arg *arg) |
||||
|
{ |
||||
|
ssize_t ret; |
||||
|
size_t len = arg->u2.s & ~INSIDE_HEADER_BIT; |
||||
|
be16 hdr = cpu_to_be16(tal_count(arg->u1.const_vp)); |
||||
|
|
||||
|
ret = write(fd, (char *)&hdr + len, HEADER_LEN - len); |
||||
|
if (ret <= 0) |
||||
|
return -1; |
||||
|
arg->u2.s += ret; |
||||
|
|
||||
|
/* Both bytes written? Set up for normal write of data. */ |
||||
|
if (arg->u2.s == INSIDE_HEADER_BIT + HEADER_LEN) |
||||
|
arg->u2.s = be16_to_cpu(hdr); |
||||
|
|
||||
|
return arg->u2.s == 0; |
||||
|
} |
||||
|
|
||||
|
static int do_write_wire(int fd, struct io_plan_arg *arg) |
||||
|
{ |
||||
|
ssize_t ret; |
||||
|
|
||||
|
/* Still writing header? */ |
||||
|
if (arg->u2.s & INSIDE_HEADER_BIT) |
||||
|
return do_write_wire_header(fd, arg); |
||||
|
|
||||
|
/* Normal write */ |
||||
|
ret = write(fd, arg->u1.cp, arg->u2.s); |
||||
|
if (ret <= 0) |
||||
|
return -1; |
||||
|
|
||||
|
arg->u1.cp += ret; |
||||
|
arg->u2.s -= ret; |
||||
|
return arg->u2.s == 0; |
||||
|
} |
||||
|
|
||||
|
/* Write message from data (tal_count(data) gives length). */ |
||||
|
struct io_plan *io_write_wire_(struct io_conn *conn, |
||||
|
const u8 *data, |
||||
|
struct io_plan *(*next)(struct io_conn *, void *), |
||||
|
void *next_arg) |
||||
|
{ |
||||
|
struct io_plan_arg *arg = io_plan_arg(conn, IO_OUT); |
||||
|
|
||||
|
arg->u1.const_vp = data; |
||||
|
|
||||
|
/* We use u2 to store the length we've written. */ |
||||
|
arg->u2.s = INSIDE_HEADER_BIT; |
||||
|
return io_set_plan(conn, IO_OUT, do_write_wire, next, next_arg); |
||||
|
} |
@ -0,0 +1,31 @@ |
|||||
|
#ifndef LIGHTNING_WIRE_WIRE_IO_H |
||||
|
#define LIGHTNING_WIRE_WIRE_IO_H |
||||
|
#include "config.h" |
||||
|
#include <ccan/io/io.h> |
||||
|
#include <ccan/short_types/short_types.h> |
||||
|
|
||||
|
/* Read message into *data, allocating off ctx. */ |
||||
|
struct io_plan *io_read_wire_(struct io_conn *conn, |
||||
|
const tal_t *ctx, |
||||
|
u8 **data, |
||||
|
struct io_plan *(*next)(struct io_conn *, void *), |
||||
|
void *next_arg); |
||||
|
|
||||
|
#define io_read_wire(conn, ctx, data, next, arg) \ |
||||
|
io_read_wire_((conn), (ctx), (data), \ |
||||
|
typesafe_cb_preargs(struct io_plan *, void *, \ |
||||
|
(next), (arg), struct io_conn *), \ |
||||
|
(arg)) |
||||
|
|
||||
|
/* Write message from data (tal_count(data) gives length). */ |
||||
|
struct io_plan *io_write_wire_(struct io_conn *conn, |
||||
|
const u8 *data, |
||||
|
struct io_plan *(*next)(struct io_conn *, void *), |
||||
|
void *next_arg); |
||||
|
|
||||
|
#define io_write_wire(conn, data, next, arg) \ |
||||
|
io_write_wire_((conn), (data), \ |
||||
|
typesafe_cb_preargs(struct io_plan *, void *, \ |
||||
|
(next), (arg), struct io_conn *), \ |
||||
|
(arg)) |
||||
|
#endif /* LIGHTNING_WIRE_WIRE_IO_H */ |
Loading…
Reference in new issue