summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorH. Peter Anvin <hpa@zytor.com>2010-02-07 15:14:21 -0800
committerH. Peter Anvin <hpa@zytor.com>2010-02-07 15:14:21 -0800
commit88f5f9b480f036ef9da9cf9deb41935c9ad9743c (patch)
treed4aae48e5a3f2b8d618ca5675c87f852d744ac46
parent257adb86a34db443100d9e18891ce98f2518333f (diff)
downloadsyslinux-88f5f9b480f036ef9da9cf9deb41935c9ad9743c.tar.gz
sysdump: change to a two-phase generate/output model, buffer in memory
Instead of outputting data as it is generated, buffer it all in memory and then output it all at once. This not only gives us exact size and so on before we start, but turns out to be faster at least for TFTP. Signed-off-by: H. Peter Anvin <hpa@zytor.com>
-rw-r--r--com32/sysdump/backend.h30
-rw-r--r--com32/sysdump/be_null.c30
-rw-r--r--com32/sysdump/be_tftp.c82
-rw-r--r--com32/sysdump/be_ymodem.c116
-rw-r--r--com32/sysdump/cpio.c17
-rw-r--r--com32/sysdump/dmi.c8
-rw-r--r--com32/sysdump/main.c17
-rw-r--r--com32/sysdump/memmap.c2
-rw-r--r--com32/sysdump/serial.c2
-rw-r--r--com32/sysdump/zout.c72
10 files changed, 183 insertions, 193 deletions
diff --git a/com32/sysdump/backend.h b/com32/sysdump/backend.h
index 0be80c63..21a9ff83 100644
--- a/com32/sysdump/backend.h
+++ b/com32/sysdump/backend.h
@@ -15,41 +15,26 @@ struct backend {
const char *helpmsg;
int minargs;
- unsigned int blocksize;
- unsigned int flags;
-
size_t dbytes;
size_t zbytes;
+ const char **argv;
- int (*open)(struct backend *, const char *argv[], size_t len);
- int (*write)(struct backend *, const char *buf, size_t len);
+ int (*write)(struct backend *);
z_stream zstream;
char *outbuf;
-
- union {
- struct {
- uint32_t my_ip;
- uint32_t srv_ip;
- uint16_t my_port;
- uint16_t srv_port;
- uint16_t seq;
- } tftp;
- struct {
- struct serial_if serial;
- uint16_t seq;
- } ymodem;
- };
+ size_t alloc;
};
/* zout.c */
-int init_data(struct backend *be, const char *argv[], size_t len);
-int write_data(struct backend *be, const void *buf, size_t len, bool flush);
+int init_data(struct backend *be, const char *argv[]);
+int write_data(struct backend *be, const void *buf, size_t len);
+int flush_data(struct backend *be);
/* cpio.c */
int cpio_hdr(struct backend *be, uint32_t mode, size_t datalen,
const char *filename);
-int cpio_init(struct backend *be, const char *argv[], size_t len);
+int cpio_init(struct backend *be, const char *argv[]);
int cpio_mkdir(struct backend *be, const char *filename);
int cpio_writefile(struct backend *be, const char *filename,
const void *data, size_t len);
@@ -63,6 +48,5 @@ struct backend *get_backend(const char *name);
/* backends */
extern struct backend be_tftp;
extern struct backend be_ymodem;
-extern struct backend be_null;
#endif /* BACKEND_H */
diff --git a/com32/sysdump/be_null.c b/com32/sysdump/be_null.c
deleted file mode 100644
index 4aab99ce..00000000
--- a/com32/sysdump/be_null.c
+++ /dev/null
@@ -1,30 +0,0 @@
-/*
- * Null data output backend
- */
-
-#include "backend.h"
-
-static int be_null_open(struct backend *be, const char *argv[], size_t len)
-{
- (void)be;
- (void)argv;
- (void)len;
- return 0;
-}
-
-static int be_null_write(struct backend *be, const char *buf, size_t len)
-{
- (void)be;
- (void)buf;
- (void)len;
- return 0;
-}
-
-struct backend be_null = {
- .name = "null",
- .helpmsg = "",
- .minargs = 0,
- .blocksize = 32768, /* arbitrary */
- .open = be_null_open,
- .write = be_null_write,
-};
diff --git a/com32/sysdump/be_tftp.c b/com32/sysdump/be_tftp.c
index e9a98873..79642385 100644
--- a/com32/sysdump/be_tftp.c
+++ b/com32/sysdump/be_tftp.c
@@ -18,9 +18,16 @@ enum tftp_opcode {
TFTP_ERROR = 5,
};
-static uint16_t local_port = 0x4000;
+struct tftp_state {
+ uint32_t my_ip;
+ uint32_t srv_ip;
+ uint16_t my_port;
+ uint16_t srv_port;
+ uint16_t seq;
+};
-static int send_ack_packet(struct backend *be, const void *pkt, size_t len)
+static int send_ack_packet(struct tftp_state *tftp,
+ const void *pkt, size_t len)
{
com32sys_t ireg, oreg;
t_PXENV_UDP_WRITE *uw = __com32.cs_bounce;
@@ -38,9 +45,9 @@ static int send_ack_packet(struct backend *be, const void *pkt, size_t len)
for (timeout = timeouts ; *timeout ; timeout++) {
memset(uw, 0, sizeof uw);
memcpy(uw+1, pkt, len);
- uw->ip = be->tftp.srv_ip;
- uw->src_port = be->tftp.my_port;
- uw->dst_port = be->tftp.srv_port ? be->tftp.srv_port : htons(69);
+ uw->ip = tftp->srv_ip;
+ uw->src_port = tftp->my_port;
+ uw->dst_port = tftp->srv_port ? tftp->srv_port : htons(69);
uw->buffer_size = len;
uw->buffer = FAR_PTR(uw+1);
@@ -54,10 +61,10 @@ static int send_ack_packet(struct backend *be, const void *pkt, size_t len)
do {
memset(ur, 0, sizeof ur);
- ur->src_ip = be->tftp.srv_ip;
- ur->dest_ip = be->tftp.my_ip;
- ur->s_port = be->tftp.srv_port;
- ur->d_port = be->tftp.my_port;
+ ur->src_ip = tftp->srv_ip;
+ ur->dest_ip = tftp->my_ip;
+ ur->s_port = tftp->srv_port;
+ ur->d_port = tftp->my_port;
ur->buffer_size = __com32.cs_bounce_size - sizeof *ur;
ur->buffer = FAR_PTR(ur+1);
@@ -68,13 +75,13 @@ static int send_ack_packet(struct backend *be, const void *pkt, size_t len)
if (!(oreg.eflags.l & EFLAGS_CF) &&
ur->status == PXENV_STATUS_SUCCESS &&
- be->tftp.srv_ip == ur->src_ip &&
- (be->tftp.srv_port == 0 ||
- be->tftp.srv_port == ur->s_port)) {
+ tftp->srv_ip == ur->src_ip &&
+ (tftp->srv_port == 0 ||
+ tftp->srv_port == ur->s_port)) {
uint16_t *xb = (uint16_t *)(ur+1);
if (ntohs(xb[0]) == TFTP_ACK &&
- ntohs(xb[1]) == be->tftp.seq) {
- be->tftp.srv_port = ur->s_port;
+ ntohs(xb[1]) == tftp->seq) {
+ tftp->srv_port = ur->s_port;
return 0; /* All good! */
} else if (ntohs(xb[1]) == TFTP_ERROR) {
return -1; /* All bad! */
@@ -86,47 +93,50 @@ static int send_ack_packet(struct backend *be, const void *pkt, size_t len)
return -1; /* No success... */
}
-static int be_tftp_open(struct backend *be, const char *argv[], size_t len)
+static int be_tftp_write(struct backend *be)
{
+ static uint16_t local_port = 0x4000;
+ struct tftp_state tftp;
char buffer[512+4+6];
int nlen;
const union syslinux_derivative_info *sdi =
syslinux_derivative_info();
+ const char *data = be->outbuf;
+ size_t len = be->zbytes;
+ size_t chunk;
- (void)len;
-
- be->tftp.my_ip = sdi->pxe.myip;
- be->tftp.my_port = htons(local_port++);
- be->tftp.srv_ip = pxe_dns(argv[1]);
- be->tftp.srv_port = 0;
- be->tftp.seq = 0;
+ tftp.my_ip = sdi->pxe.myip;
+ tftp.my_port = htons(local_port++);
+ tftp.srv_ip = pxe_dns(be->argv[1]);
+ tftp.srv_port = 0;
+ tftp.seq = 0;
buffer[0] = 0;
buffer[1] = TFTP_WRQ;
- nlen = strlcpy(buffer+2, argv[0], 512);
+ nlen = strlcpy(buffer+2, be->argv[0], 512);
memcpy(buffer+3+nlen, "octet", 6);
- return send_ack_packet(be, buffer, 2+nlen+1+6);
-}
+ if (send_ack_packet(&tftp, buffer, 2+nlen+1+6))
+ return -1;
-static int be_tftp_write(struct backend *be, const char *buf, size_t len)
-{
- char buffer[512+4];
+ do {
+ chunk = len >= 512 ? 512 : len;
- buffer[0] = 0;
- buffer[1] = TFTP_DATA;
- *((uint16_t *)(buffer+2)) = htons(++be->tftp.seq);
- memcpy(buffer+4, buf, len);
+ buffer[1] = TFTP_DATA;
+ *((uint16_t *)(buffer+2)) = htons(++tftp.seq);
+ memcpy(buffer+4, data, chunk);
+ data += chunk;
+
+ if (send_ack_packet(&tftp, buffer, chunk+4))
+ return -1;
+ } while (chunk == 512);
- return send_ack_packet(be, buffer, len+4);
+ return 0;
}
struct backend be_tftp = {
.name = "tftp",
.helpmsg = "filename tftp_server",
.minargs = 2,
- .blocksize = 512,
- .flags = 0,
- .open = be_tftp_open,
.write = be_tftp_write,
};
diff --git a/com32/sysdump/be_ymodem.c b/com32/sysdump/be_ymodem.c
index 632758d9..b36bd039 100644
--- a/com32/sysdump/be_ymodem.c
+++ b/com32/sysdump/be_ymodem.c
@@ -17,6 +17,11 @@ enum {
CAN = 0x18,
};
+struct ymodem_state {
+ struct serial_if serial;
+ uint16_t seq;
+};
+
/*
* Append a CRC16 to a block
*/
@@ -65,101 +70,102 @@ static void add_crc16(uint8_t * blk, int len)
*blk = crc;
}
-static void send_ack(struct backend *be, const uint8_t *blk, size_t bytes);
+static void send_ack(struct ymodem_state *ym, const uint8_t *blk,
+ size_t bytes);
-static void send_ack_blk(struct backend *be, uint8_t *blk)
+static void send_ack_blk(struct ymodem_state *ym, uint8_t *blk)
{
- printf("Sending block %u...\r", be->ymodem.seq);
+ printf("Sending block %u...\r", ym->seq);
blk[0] = STX;
- blk[1] = be->ymodem.seq++;
+ blk[1] = ym->seq++;
blk[2] = ~blk[1];
add_crc16(blk+3, 1024);
- send_ack(be, blk, 1024+5);
+ send_ack(ym, blk, 1024+5);
}
-static void send_ack(struct backend *be, const uint8_t *blk, size_t bytes)
+static void send_ack(struct ymodem_state *ym, const uint8_t *blk, size_t bytes)
{
uint8_t ack_buf;
- serial_write(&be->ymodem.serial, blk, bytes);
+ serial_write(&ym->serial, blk, bytes);
do {
do {
- serial_read(&be->ymodem.serial, &ack_buf, 1);
+ serial_read(&ym->serial, &ack_buf, 1);
} while (ack_buf != ACK && ack_buf != NAK);
} while (ack_buf == NAK);
}
-static int be_ymodem_open(struct backend *be, const char *argv[], size_t len)
+static int be_ymodem_write(struct backend *be)
{
- uint8_t ack_buf, blk_buf[1024 + 5];
+ static const uint8_t eot_buf = EOT;
+ uint8_t ack_buf;
+ uint8_t blk_buf[1024 + 5];
+ struct ymodem_state ym;
+ const char *buf;
+ size_t len, chunk;
- be->ymodem.seq = 0;
+ ym.seq = 0;
/* Initialize serial port */
- if (serial_init(&be->ymodem.serial, argv+1))
+ if (serial_init(&ym.serial, &be->argv[1]))
return -1;
-
+
/* Wait for initial handshake */
printf("Waiting for handshake...\n");
do {
- serial_read(&be->ymodem.serial, &ack_buf, 1);
+ serial_read(&ym.serial, &ack_buf, 1);
} while (ack_buf != 'C');
- snprintf((char *)blk_buf+3, 1024, "%s%c%zu", argv[0], 0, len);
- send_ack_blk(be, blk_buf);
+ /* Send filename block */
+ snprintf((char *)blk_buf+3, 1024, "%s%c%zu", be->argv[0], 0, be->zbytes);
+ send_ack_blk(&ym, blk_buf);
- return 0;
-}
-
-static int be_ymodem_write(struct backend *be, const char *buf, size_t len)
-{
- static const uint8_t eot_buf = EOT;
- uint8_t ack_buf;
- uint8_t blk_buf[1024 + 5];
-
- memcpy(blk_buf+3, buf, len);
- if (len < 1024)
- memset(blk_buf+3+1024-len, 0x1a, 1024-len);
+ buf = be->outbuf;
+ len = be->zbytes;
- send_ack_blk(be, blk_buf);
+ while (len) {
+ chunk = len < 1024 ? len : 1024;
- if (len < 1024) {
- printf("\nSending EOT...\n");
- send_ack(be, &eot_buf, 1);
+ memcpy(blk_buf+3, buf, chunk);
+ if (chunk < 1024)
+ memset(blk_buf+3+1024-chunk, 0x1a, 1024-chunk);
- printf("Waiting for handshake...\n");
- do {
- serial_read(&be->ymodem.serial, &ack_buf, 1);
- } while (ack_buf != 'C');
- be->ymodem.seq = 0;
-
- printf("Sending batch termination block...\n");
- memset(blk_buf+3, 0, 128);
- blk_buf[0] = SOH;
- blk_buf[1] = 0;
- blk_buf[2] = 0xff;
- add_crc16(blk_buf + 3, 128);
- serial_write(&be->ymodem.serial, blk_buf, 128 + 5);
- /*
- * rb doesn't seem to ack the EOT for an end batch transfer,
- * contrary to spec.
- */
- printf("Done.\n");
- serial_cleanup(&be->ymodem.serial);
+ send_ack_blk(&ym, blk_buf);
+ len -= chunk;
}
+ printf("\nSending EOT...\n");
+ send_ack(&ym, &eot_buf, 1);
+
+ printf("Waiting for handshake...\n");
+ do {
+ serial_read(&ym.serial, &ack_buf, 1);
+ } while (ack_buf != 'C');
+ ym.seq = 0;
+
+ printf("Sending batch termination block...\n");
+ memset(blk_buf+3, 0, 128);
+ blk_buf[0] = SOH;
+ blk_buf[1] = 0;
+ blk_buf[2] = 0xff;
+ add_crc16(blk_buf + 3, 128);
+ serial_write(&ym.serial, blk_buf, 128 + 5);
+ /*
+ * rb doesn't seem to ack the EOT for an end batch transfer,
+ * contrary to spec.
+ */
+ printf("Done.\n");
+ serial_cleanup(&ym.serial);
+
return 0;
-}
+}
struct backend be_ymodem = {
.name = "ymodem",
.helpmsg = "filename port [speed]",
.minargs = 2,
- .blocksize = 1024,
- .flags = BE_NEEDLEN,
- .open = be_ymodem_open,
.write = be_ymodem_write,
};
diff --git a/com32/sysdump/cpio.c b/com32/sysdump/cpio.c
index 30e3d496..dfc7c946 100644
--- a/com32/sysdump/cpio.c
+++ b/com32/sysdump/cpio.c
@@ -18,7 +18,7 @@ int cpio_pad(struct backend *be)
{
static char pad[4]; /* Up to 4 zero bytes */
if (be->dbytes & 3)
- return write_data(be, pad, -be->dbytes & 3, false);
+ return write_data(be, pad, -be->dbytes & 3);
else
return 0;
}
@@ -48,16 +48,16 @@ int cpio_hdr(struct backend *be, uint32_t mode, size_t datalen,
0, /* c_rmin */
nlen, /* c_namesize */
0); /* c_chksum */
- rv |= write_data(be, hdr, 6+13*8, false);
- rv |= write_data(be, filename, nlen, false);
+ rv |= write_data(be, hdr, 6+13*8);
+ rv |= write_data(be, filename, nlen);
rv |= cpio_pad(be);
return rv;
}
-int cpio_init(struct backend *be, const char *argv[], size_t len)
+int cpio_init(struct backend *be, const char *argv[])
{
now = posix_time();
- return init_data(be, argv, len);
+ return init_data(be, argv);
}
int cpio_mkdir(struct backend *be, const char *filename)
@@ -71,7 +71,7 @@ int cpio_writefile(struct backend *be, const char *filename,
int rv;
rv = cpio_hdr(be, MODE_FILE, len, filename);
- rv |= write_data(be, data, len, false);
+ rv |= write_data(be, data, len);
rv |= cpio_pad(be);
return rv;
@@ -79,8 +79,5 @@ int cpio_writefile(struct backend *be, const char *filename,
int cpio_close(struct backend *be)
{
- int rv;
- rv = cpio_hdr(be, 0, 0, "TRAILER!!!");
- rv |= write_data(be, NULL, 0, true);
- return rv;
+ return cpio_hdr(be, 0, 0, "TRAILER!!!");
}
diff --git a/com32/sysdump/dmi.c b/com32/sysdump/dmi.c
index 64f95a9a..be4cce46 100644
--- a/com32/sysdump/dmi.c
+++ b/com32/sysdump/dmi.c
@@ -78,8 +78,8 @@ static void dump_smbios(struct backend *be, size_t dptr)
smx.dmi.tbladdr = sizeof smx;
smx.dmi.csum -= checksum(&smx.dmi, 0x0f);
- write_data(be, &smx, sizeof smx, false);
- write_data(be, (const void *)smb->dmi.tbladdr, smb->dmi.tbllen, false);
+ write_data(be, &smx, sizeof smx);
+ write_data(be, (const void *)smb->dmi.tbladdr, smb->dmi.tbllen);
}
static void dump_old_dmi(struct backend *be, size_t dptr)
@@ -104,8 +104,8 @@ static void dump_old_dmi(struct backend *be, size_t dptr)
fake.dmi.tbladdr = sizeof fake;
fake.dmi.csum -= checksum(&fake.dmi, 0x0f);
- write_data(be, &fake, sizeof fake, false);
- write_data(be, (const void *)dmi->tbladdr, dmi->tbllen, false);
+ write_data(be, &fake, sizeof fake);
+ write_data(be, (const void *)dmi->tbladdr, dmi->tbllen);
}
void dump_dmi(struct backend *be)
diff --git a/com32/sysdump/main.c b/com32/sysdump/main.c
index 4c58f5f4..19245061 100644
--- a/com32/sysdump/main.c
+++ b/com32/sysdump/main.c
@@ -31,11 +31,11 @@ __noreturn die(const char *msg)
exit(1);
}
-static void dump_all(struct backend *be, const char *argv[], size_t len)
+static void dump_all(struct backend *be, const char *argv[])
{
static const char version[] = "SYSDUMP " VERSION_STR " " DATE "\n";
- cpio_init(be, argv, len);
+ cpio_init(be, argv);
cpio_writefile(be, "sysdump", version, sizeof version);
@@ -47,13 +47,15 @@ static void dump_all(struct backend *be, const char *argv[], size_t len)
dump_vesa_tables(be);
cpio_close(be);
+ printf("Uploading data... ");
+ flush_data(be);
+ printf("done.\n");
}
static struct backend *backends[] =
{
&be_tftp,
&be_ymodem,
- &be_null,
NULL
};
@@ -88,11 +90,8 @@ int main(int argc, char *argv[])
/* Do this as early as possible */
snapshot_lowmem();
- if (be->flags & BE_NEEDLEN) {
- dump_all(&be_null, NULL, 0);
- dump_all(be, (const char **)argv + 2, be_null.zbytes);
- } else {
- dump_all(be, (const char **)argv + 2, 0);
- }
+ /* Do the actual data dump */
+ dump_all(be, (const char **)argv + 2);
+
return 0;
}
diff --git a/com32/sysdump/memmap.c b/com32/sysdump/memmap.c
index c8cfc032..a85f0925 100644
--- a/com32/sysdump/memmap.c
+++ b/com32/sysdump/memmap.c
@@ -33,7 +33,7 @@ static void dump_e820(struct backend *be)
ireg.ecx.l = sizeof curr->data;
ireg.es = SEG(curr->data);
ireg.edi.w[0] = OFFS(curr->data);
-
+
do {
__intcall(0x15, &ireg, &oreg);
if ((oreg.eflags.l & EFLAGS_CF) ||
diff --git a/com32/sysdump/serial.c b/com32/sysdump/serial.c
index f96ae3fa..71b3067a 100644
--- a/com32/sysdump/serial.c
+++ b/com32/sysdump/serial.c
@@ -122,5 +122,3 @@ void serial_cleanup(struct serial_if *sif)
if ((sif->old.iir & 0xc0) != 0xc0)
outb(0x00, port + FCR); /* Disable FIFOs */
}
-
-
diff --git a/com32/sysdump/zout.c b/com32/sysdump/zout.c
index d3be893d..61dd514d 100644
--- a/com32/sysdump/zout.c
+++ b/com32/sysdump/zout.c
@@ -10,19 +10,21 @@
#include <zlib.h>
#include "backend.h"
-int init_data(struct backend *be, const char *argv[], size_t len)
+#define ALLOC_CHUNK 65536
+
+int init_data(struct backend *be, const char *argv[])
{
- be->outbuf = malloc(be->blocksize);
- if (!be->outbuf)
- return -1;
+ be->argv = argv;
- if (be->open(be, argv, len))
+ be->alloc = ALLOC_CHUNK;
+ be->outbuf = malloc(ALLOC_CHUNK);
+ if (!be->outbuf)
return -1;
memset(&be->zstream, 0, sizeof be->zstream);
be->zstream.next_out = (void *)be->outbuf;
- be->zstream.avail_out = be->blocksize;
+ be->zstream.avail_out = ALLOC_CHUNK;
be->dbytes = be->zbytes = 0;
@@ -34,7 +36,27 @@ int init_data(struct backend *be, const char *argv[], size_t len)
return 0;
}
-int write_data(struct backend *be, const void *buf, size_t len, bool flush)
+static int do_deflate(struct backend *be, int flush)
+{
+ int rv;
+
+ rv = deflate(&be->zstream, flush);
+ be->zbytes = be->alloc - be->zstream.avail_out;
+ if (be->zstream.avail_out == 0) {
+ char *buf;
+ buf = realloc(be->outbuf, be->alloc + ALLOC_CHUNK);
+ if (!buf)
+ return Z_MEM_ERROR;
+ be->outbuf = buf;
+ be->zstream.next_out = (void *)(buf + be->zbytes);
+ be->zstream.avail_out = be->alloc - be->zbytes;
+ }
+
+ return rv;
+}
+
+
+int write_data(struct backend *be, const void *buf, size_t len)
{
int rv = Z_OK;
@@ -42,27 +64,31 @@ int write_data(struct backend *be, const void *buf, size_t len, bool flush)
be->zstream.avail_in = len;
be->dbytes += len;
-
- while (be->zstream.avail_in || (flush && rv == Z_OK)) {
- rv = deflate(&be->zstream, flush ? Z_FINISH : Z_NO_FLUSH);
- if (be->zstream.avail_out == 0) {
- if (be->write(be, be->outbuf, be->blocksize))
- return -1;
- be->zbytes += be->blocksize;
- be->zstream.next_out = (void *)be->outbuf;
- be->zstream.avail_out = be->blocksize;
- }
- if (rv == Z_STREAM_ERROR)
+
+ while (be->zstream.avail_in) {
+ rv = do_deflate(be, Z_NO_FLUSH);
+ if (rv < 0)
return -1;
}
+ return 0;
+}
- if (flush) {
- /* Output the last (fractional) packet... may be zero */
- if (be->write(be, be->outbuf, be->blocksize - be->zstream.avail_out))
+/* Output the data and shut down the stream */
+int flush_data(struct backend *be)
+{
+ int rv = Z_OK;
+
+ while (rv == Z_OK) {
+ rv = do_deflate(be, Z_FINISH);
+ if (rv < 0)
return -1;
- be->zbytes += be->blocksize - be->zstream.avail_out;
- free(be->outbuf);
}
+ if (be->write(be))
+ return -1;
+
+ free(be->outbuf);
+ be->outbuf = NULL;
+ be->dbytes = be->zbytes = be->alloc = 0;
return 0;
}