778 lines
		
	
	
		
			20 KiB
		
	
	
	
		
			PHP
		
	
	
	
			
		
		
	
	
			778 lines
		
	
	
		
			20 KiB
		
	
	
	
		
			PHP
		
	
	
	
<?php
 | 
						|
 | 
						|
function tokenize ($str, &$out) {
 | 
						|
    $out = array();
 | 
						|
 | 
						|
    while (strlen($str) > 0) {
 | 
						|
        if (preg_match('/^\\/\\/.*/', $str, $matches)) {
 | 
						|
            $str = substr($str, strlen($matches[0]));
 | 
						|
        }
 | 
						|
        else if (preg_match('/^\\s+/', $str, $matches)) {
 | 
						|
            $str = substr($str, strlen($matches[0]));
 | 
						|
        }
 | 
						|
        else if (preg_match('/^include/', $str, $matches)) {
 | 
						|
            $out[] = array('include', null);
 | 
						|
            $str = substr($str, strlen($matches[0]));
 | 
						|
        }
 | 
						|
        else if (preg_match('/^message/', $str, $matches)) {
 | 
						|
            $out[] = array('message', null);
 | 
						|
            $str = substr($str, strlen($matches[0]));
 | 
						|
        }
 | 
						|
        else if (preg_match('/^repeated/', $str, $matches)) {
 | 
						|
            $out[] = array('repeated', null);
 | 
						|
            $str = substr($str, strlen($matches[0]));
 | 
						|
        }
 | 
						|
        else if (preg_match('/^required/', $str, $matches)) {
 | 
						|
            $out[] = array('required', null);
 | 
						|
            $str = substr($str, strlen($matches[0]));
 | 
						|
        }
 | 
						|
        else if (preg_match('/^optional/', $str, $matches)) {
 | 
						|
            $out[] = array('optional', null);
 | 
						|
            $str = substr($str, strlen($matches[0]));
 | 
						|
        }
 | 
						|
        else if (preg_match('/^{/', $str, $matches)) {
 | 
						|
            $out[] = array('spar', null);
 | 
						|
            $str = substr($str, strlen($matches[0]));
 | 
						|
        }
 | 
						|
        else if (preg_match('/^}/', $str, $matches)) {
 | 
						|
            $out[] = array('epar', null);
 | 
						|
            $str = substr($str, strlen($matches[0]));
 | 
						|
        }
 | 
						|
        else if (preg_match('/^\(/', $str, $matches)) {
 | 
						|
            $out[] = array('srpar', null);
 | 
						|
            $str = substr($str, strlen($matches[0]));
 | 
						|
        }
 | 
						|
        else if (preg_match('/^\)/', $str, $matches)) {
 | 
						|
            $out[] = array('erpar', null);
 | 
						|
            $str = substr($str, strlen($matches[0]));
 | 
						|
        }
 | 
						|
        else if (preg_match('/^=/', $str, $matches)) {
 | 
						|
            $out[] = array('equals', null);
 | 
						|
            $str = substr($str, strlen($matches[0]));
 | 
						|
        }
 | 
						|
        else if (preg_match('/^;/', $str, $matches)) {
 | 
						|
            $out[] = array('semicolon', null);
 | 
						|
            $str = substr($str, strlen($matches[0]));
 | 
						|
        }
 | 
						|
        else if (preg_match('/^uint(8|16|32|64)/', $str, $matches)) {
 | 
						|
            $out[] = array('uint', $matches[1]);
 | 
						|
            $str = substr($str, strlen($matches[0]));
 | 
						|
        }
 | 
						|
        else if (preg_match('/^data/', $str, $matches)) {
 | 
						|
            $out[] = array('data', null);
 | 
						|
            $str = substr($str, strlen($matches[0]));
 | 
						|
        }
 | 
						|
        else if (preg_match('/^[0-9]+/', $str, $matches)) {
 | 
						|
            $out[] = array('number', $matches[0]);
 | 
						|
            $str = substr($str, strlen($matches[0]));
 | 
						|
        }
 | 
						|
        else if (preg_match('/^[a-zA-Z_][a-zA-Z0-9_]*/', $str, $matches)) {
 | 
						|
            $out[] = array('name', $matches[0]);
 | 
						|
            $str = substr($str, strlen($matches[0]));
 | 
						|
        }
 | 
						|
        else if (preg_match('/^"([^"]*)"/', $str, $matches)) {
 | 
						|
            $out[] = array('string', $matches[1]);
 | 
						|
            $str = substr($str, strlen($matches[0]));
 | 
						|
        }
 | 
						|
        else {
 | 
						|
            return FALSE;
 | 
						|
        }
 | 
						|
    }
 | 
						|
 | 
						|
    return TRUE;
 | 
						|
}
 | 
						|
 | 
						|
function fatal_error ($message)
 | 
						|
{
 | 
						|
    fwrite(STDERR, "Fatal error: $message\n");
 | 
						|
 | 
						|
    ob_get_clean();
 | 
						|
    exit(1);
 | 
						|
}
 | 
						|
 | 
						|
function make_writer_decl ($msg, $entry)
 | 
						|
{
 | 
						|
    switch ($entry["type"]["type"]) {
 | 
						|
        case "uint":
 | 
						|
            return "void {$msg["name"]}Writer_Add{$entry["name"]} ({$msg["name"]}Writer *o, uint{$entry["type"]["size"]}_t v)";
 | 
						|
        case "data":
 | 
						|
            return "uint8_t * {$msg["name"]}Writer_Add{$entry["name"]} ({$msg["name"]}Writer *o, int len)";
 | 
						|
        case "constdata":
 | 
						|
            return "uint8_t * {$msg["name"]}Writer_Add{$entry["name"]} ({$msg["name"]}Writer *o)";
 | 
						|
        default:
 | 
						|
            assert(0);
 | 
						|
    }
 | 
						|
}
 | 
						|
 | 
						|
function make_parser_decl ($msg, $entry)
 | 
						|
{
 | 
						|
    switch ($entry["type"]["type"]) {
 | 
						|
        case "uint":
 | 
						|
            return "int {$msg["name"]}Parser_Get{$entry["name"]} ({$msg["name"]}Parser *o, uint{$entry["type"]["size"]}_t *v)";
 | 
						|
        case "data":
 | 
						|
            return "int {$msg["name"]}Parser_Get{$entry["name"]} ({$msg["name"]}Parser *o, uint8_t **data, int *data_len)";
 | 
						|
        case "constdata":
 | 
						|
            return "int {$msg["name"]}Parser_Get{$entry["name"]} ({$msg["name"]}Parser *o, uint8_t **data)";
 | 
						|
        default:
 | 
						|
            assert(0);
 | 
						|
    }
 | 
						|
}
 | 
						|
 | 
						|
function make_parser_reset_decl ($msg, $entry)
 | 
						|
{
 | 
						|
    return "void {$msg["name"]}Parser_Reset{$entry["name"]} ({$msg["name"]}Parser *o)";
 | 
						|
}
 | 
						|
 | 
						|
function make_parser_forward_decl ($msg, $entry)
 | 
						|
{
 | 
						|
    return "void {$msg["name"]}Parser_Forward{$entry["name"]} ({$msg["name"]}Parser *o)";
 | 
						|
}
 | 
						|
 | 
						|
function make_type_name ($msg, $entry)
 | 
						|
{
 | 
						|
    switch ($entry["type"]["type"]) {
 | 
						|
        case "uint":
 | 
						|
            return "BPROTO_TYPE_UINT{$entry["type"]["size"]}";
 | 
						|
        case "data":
 | 
						|
            return "BPROTO_TYPE_DATA";
 | 
						|
        case "constdata":
 | 
						|
            return "BPROTO_TYPE_CONSTDATA";
 | 
						|
        default:
 | 
						|
            assert(0);
 | 
						|
    }
 | 
						|
}
 | 
						|
 | 
						|
function make_finish_assert ($msg, $entry)
 | 
						|
{
 | 
						|
    switch ($entry["cardinality"]) {
 | 
						|
        case "repeated":
 | 
						|
            return "ASSERT(o->{$entry["name"]}_count >= 0)";
 | 
						|
        case "required repeated":
 | 
						|
            return "ASSERT(o->{$entry["name"]}_count >= 1)";
 | 
						|
        case "optional":
 | 
						|
            return "ASSERT(o->{$entry["name"]}_count >= 0 && o->{$entry["name"]}_count <= 1)";
 | 
						|
        case "required":
 | 
						|
            return "ASSERT(o->{$entry["name"]}_count == 1)";
 | 
						|
        default:
 | 
						|
            assert(0);
 | 
						|
    }
 | 
						|
}
 | 
						|
 | 
						|
function make_add_count_assert ($msg, $entry)
 | 
						|
{
 | 
						|
    if (in_array($entry["cardinality"], array("optional", "required"))) {
 | 
						|
        return "ASSERT(o->{$entry["name"]}_count == 0)";
 | 
						|
    }
 | 
						|
    return "";
 | 
						|
}
 | 
						|
 | 
						|
function make_add_length_assert ($msg, $entry)
 | 
						|
{
 | 
						|
    if ($entry["type"]["type"] == "data") {
 | 
						|
        return "ASSERT(len >= 0 && len <= UINT32_MAX)";
 | 
						|
    }
 | 
						|
    return "";
 | 
						|
}
 | 
						|
 | 
						|
function make_size_define ($msg, $entry)
 | 
						|
{
 | 
						|
    switch ($entry["type"]["type"]) {
 | 
						|
        case "uint":
 | 
						|
            return "#define {$msg["name"]}_SIZE{$entry["name"]} (sizeof(struct BProto_header_s) + sizeof(struct BProto_uint{$entry["type"]["size"]}_s))";
 | 
						|
        case "data":
 | 
						|
            return "#define {$msg["name"]}_SIZE{$entry["name"]}(_len) (sizeof(struct BProto_header_s) + sizeof(struct BProto_data_header_s) + (_len))";
 | 
						|
        case "constdata":
 | 
						|
            return "#define {$msg["name"]}_SIZE{$entry["name"]} (sizeof(struct BProto_header_s) + sizeof(struct BProto_data_header_s) + ({$entry["type"]["size"]}))";
 | 
						|
        default:
 | 
						|
            assert(0);
 | 
						|
    }
 | 
						|
}
 | 
						|
 | 
						|
function generate_header ($name, $directives, $messages) {
 | 
						|
    ob_start();
 | 
						|
 | 
						|
    echo <<<EOD
 | 
						|
/*
 | 
						|
    DO NOT EDIT THIS FILE!
 | 
						|
    This file was automatically generated by the bproto generator.
 | 
						|
*/
 | 
						|
 | 
						|
#include <stdint.h>
 | 
						|
#include <string.h>
 | 
						|
 | 
						|
#include <misc/debug.h>
 | 
						|
#include <misc/byteorder.h>
 | 
						|
#include <bproto/BProto.h>
 | 
						|
 | 
						|
 | 
						|
EOD;
 | 
						|
 | 
						|
    foreach ($directives as $directive) {
 | 
						|
        if ($directive["type"] == "include") {
 | 
						|
            echo <<<EOD
 | 
						|
#include "{$directive["file"]}"
 | 
						|
 | 
						|
EOD;
 | 
						|
        }
 | 
						|
    }
 | 
						|
 | 
						|
    echo <<<EOD
 | 
						|
 | 
						|
 | 
						|
EOD;
 | 
						|
 | 
						|
    foreach ($messages as $msg) {
 | 
						|
 | 
						|
        foreach ($msg["entries"] as $entry) {
 | 
						|
            $def = make_size_define($msg, $entry);
 | 
						|
            echo <<<EOD
 | 
						|
{$def}
 | 
						|
 | 
						|
EOD;
 | 
						|
        }
 | 
						|
 | 
						|
        echo <<<EOD
 | 
						|
 | 
						|
typedef struct {
 | 
						|
    uint8_t *out;
 | 
						|
    int used;
 | 
						|
 | 
						|
EOD;
 | 
						|
 | 
						|
        foreach ($msg["entries"] as $entry) {
 | 
						|
            echo <<<EOD
 | 
						|
    int {$entry["name"]}_count;
 | 
						|
 | 
						|
EOD;
 | 
						|
        }
 | 
						|
 | 
						|
        echo <<<EOD
 | 
						|
} {$msg["name"]}Writer;
 | 
						|
 | 
						|
static void {$msg["name"]}Writer_Init ({$msg["name"]}Writer *o, uint8_t *out);
 | 
						|
static int {$msg["name"]}Writer_Finish ({$msg["name"]}Writer *o);
 | 
						|
 | 
						|
EOD;
 | 
						|
 | 
						|
        foreach ($msg["entries"] as $entry) {
 | 
						|
            $decl = make_writer_decl($msg, $entry);
 | 
						|
            echo <<<EOD
 | 
						|
static {$decl};
 | 
						|
 | 
						|
EOD;
 | 
						|
        }
 | 
						|
 | 
						|
        echo <<<EOD
 | 
						|
 | 
						|
typedef struct {
 | 
						|
    uint8_t *buf;
 | 
						|
    int buf_len;
 | 
						|
 | 
						|
EOD;
 | 
						|
        foreach ($msg["entries"] as $entry) {
 | 
						|
            echo <<<EOD
 | 
						|
    int {$entry["name"]}_start;
 | 
						|
    int {$entry["name"]}_span;
 | 
						|
    int {$entry["name"]}_pos;
 | 
						|
 | 
						|
EOD;
 | 
						|
        }
 | 
						|
 | 
						|
        echo <<<EOD
 | 
						|
} {$msg["name"]}Parser;
 | 
						|
 | 
						|
static int {$msg["name"]}Parser_Init ({$msg["name"]}Parser *o, uint8_t *buf, int buf_len);
 | 
						|
static int {$msg["name"]}Parser_GotEverything ({$msg["name"]}Parser *o);
 | 
						|
 | 
						|
EOD;
 | 
						|
 | 
						|
        foreach ($msg["entries"] as $entry) {
 | 
						|
            $decl = make_parser_decl($msg, $entry);
 | 
						|
            $reset_decl = make_parser_reset_decl($msg, $entry);
 | 
						|
            $forward_decl = make_parser_forward_decl($msg, $entry);
 | 
						|
            echo <<<EOD
 | 
						|
static {$decl};
 | 
						|
static {$reset_decl};
 | 
						|
static {$forward_decl};
 | 
						|
 | 
						|
EOD;
 | 
						|
        }
 | 
						|
 | 
						|
        echo <<<EOD
 | 
						|
 | 
						|
void {$msg["name"]}Writer_Init ({$msg["name"]}Writer *o, uint8_t *out)
 | 
						|
{
 | 
						|
    o->out = out;
 | 
						|
    o->used = 0;
 | 
						|
 | 
						|
EOD;
 | 
						|
 | 
						|
        foreach ($msg["entries"] as $entry) {
 | 
						|
            echo <<<EOD
 | 
						|
    o->{$entry["name"]}_count = 0;
 | 
						|
 | 
						|
EOD;
 | 
						|
        }
 | 
						|
 | 
						|
        echo <<<EOD
 | 
						|
}
 | 
						|
 | 
						|
int {$msg["name"]}Writer_Finish ({$msg["name"]}Writer *o)
 | 
						|
{
 | 
						|
    ASSERT(o->used >= 0)
 | 
						|
 | 
						|
EOD;
 | 
						|
 | 
						|
        foreach ($msg["entries"] as $entry) {
 | 
						|
            $ass = make_finish_assert($msg, $entry);
 | 
						|
            echo <<<EOD
 | 
						|
    {$ass}
 | 
						|
 | 
						|
EOD;
 | 
						|
        }
 | 
						|
 | 
						|
        echo <<<EOD
 | 
						|
 | 
						|
    return o->used;
 | 
						|
}
 | 
						|
 | 
						|
 | 
						|
EOD;
 | 
						|
 | 
						|
        foreach ($msg["entries"] as $entry) {
 | 
						|
            $decl = make_writer_decl($msg, $entry);
 | 
						|
            $type = make_type_name($msg, $entry);
 | 
						|
            $add_count_assert = make_add_count_assert($msg, $entry);
 | 
						|
            $add_length_assert = make_add_length_assert($msg, $entry);
 | 
						|
 | 
						|
            echo <<<EOD
 | 
						|
{$decl}
 | 
						|
{
 | 
						|
    ASSERT(o->used >= 0)
 | 
						|
    {$add_count_assert}
 | 
						|
    {$add_length_assert}
 | 
						|
 | 
						|
    struct BProto_header_s header;
 | 
						|
    header.id = htol16({$entry["id"]});
 | 
						|
    header.type = htol16({$type});
 | 
						|
    memcpy(o->out + o->used, &header, sizeof(header));
 | 
						|
    o->used += sizeof(struct BProto_header_s);
 | 
						|
 | 
						|
 | 
						|
EOD;
 | 
						|
            switch ($entry["type"]["type"]) {
 | 
						|
                case "uint":
 | 
						|
                    echo <<<EOD
 | 
						|
    struct BProto_uint{$entry["type"]["size"]}_s data;
 | 
						|
    data.v = htol{$entry["type"]["size"]}(v);
 | 
						|
    memcpy(o->out + o->used, &data, sizeof(data));
 | 
						|
    o->used += sizeof(struct BProto_uint{$entry["type"]["size"]}_s);
 | 
						|
 | 
						|
EOD;
 | 
						|
                    break;
 | 
						|
                case "data":
 | 
						|
                    echo <<<EOD
 | 
						|
    struct BProto_data_header_s data;
 | 
						|
    data.len = htol32(len);
 | 
						|
    memcpy(o->out + o->used, &data, sizeof(data));
 | 
						|
    o->used += sizeof(struct BProto_data_header_s);
 | 
						|
 | 
						|
    uint8_t *dest = (o->out + o->used);
 | 
						|
    o->used += len;
 | 
						|
 | 
						|
EOD;
 | 
						|
                    break;
 | 
						|
                case "constdata":
 | 
						|
                    echo <<<EOD
 | 
						|
    struct BProto_data_header_s data;
 | 
						|
    data.len = htol32({$entry["type"]["size"]});
 | 
						|
    memcpy(o->out + o->used, &data, sizeof(data));
 | 
						|
    o->used += sizeof(struct BProto_data_header_s);
 | 
						|
 | 
						|
    uint8_t *dest = (o->out + o->used);
 | 
						|
    o->used += ({$entry["type"]["size"]});
 | 
						|
 | 
						|
EOD;
 | 
						|
                    break;
 | 
						|
                default:
 | 
						|
                    assert(0);
 | 
						|
            }
 | 
						|
 | 
						|
            echo <<<EOD
 | 
						|
 | 
						|
    o->{$entry["name"]}_count++;
 | 
						|
 | 
						|
EOD;
 | 
						|
            if (in_array($entry["type"]["type"], array("data", "constdata"))) {
 | 
						|
                echo <<<EOD
 | 
						|
 | 
						|
    return dest;
 | 
						|
 | 
						|
EOD;
 | 
						|
            }
 | 
						|
 | 
						|
            echo <<<EOD
 | 
						|
}
 | 
						|
 | 
						|
 | 
						|
EOD;
 | 
						|
        }
 | 
						|
 | 
						|
        echo <<<EOD
 | 
						|
int {$msg["name"]}Parser_Init ({$msg["name"]}Parser *o, uint8_t *buf, int buf_len)
 | 
						|
{
 | 
						|
    ASSERT(buf_len >= 0)
 | 
						|
 | 
						|
    o->buf = buf;
 | 
						|
    o->buf_len = buf_len;
 | 
						|
 | 
						|
EOD;
 | 
						|
 | 
						|
        foreach ($msg["entries"] as $entry) {
 | 
						|
            echo <<<EOD
 | 
						|
    o->{$entry["name"]}_start = o->buf_len;
 | 
						|
    o->{$entry["name"]}_span = 0;
 | 
						|
    o->{$entry["name"]}_pos = 0;
 | 
						|
 | 
						|
EOD;
 | 
						|
        }
 | 
						|
 | 
						|
        echo <<<EOD
 | 
						|
 | 
						|
 | 
						|
EOD;
 | 
						|
 | 
						|
        foreach ($msg["entries"] as $entry) {
 | 
						|
            echo <<<EOD
 | 
						|
    int {$entry["name"]}_count = 0;
 | 
						|
 | 
						|
EOD;
 | 
						|
        }
 | 
						|
 | 
						|
        echo <<<EOD
 | 
						|
 | 
						|
    int pos = 0;
 | 
						|
    int left = o->buf_len;
 | 
						|
 | 
						|
    while (left > 0) {
 | 
						|
        int entry_pos = pos;
 | 
						|
 | 
						|
        if (!(left >= sizeof(struct BProto_header_s))) {
 | 
						|
            return 0;
 | 
						|
        }
 | 
						|
        struct BProto_header_s header;
 | 
						|
        memcpy(&header, o->buf + pos, sizeof(header));
 | 
						|
        pos += sizeof(struct BProto_header_s);
 | 
						|
        left -= sizeof(struct BProto_header_s);
 | 
						|
        uint16_t type = ltoh16(header.type);
 | 
						|
        uint16_t id = ltoh16(header.id);
 | 
						|
 | 
						|
        switch (type) {
 | 
						|
 | 
						|
EOD;
 | 
						|
 | 
						|
        foreach (array(8, 16, 32, 64) as $bits) {
 | 
						|
            echo <<<EOD
 | 
						|
            case BPROTO_TYPE_UINT{$bits}: {
 | 
						|
                if (!(left >= sizeof(struct BProto_uint{$bits}_s))) {
 | 
						|
                    return 0;
 | 
						|
                }
 | 
						|
                pos += sizeof(struct BProto_uint{$bits}_s);
 | 
						|
                left -= sizeof(struct BProto_uint{$bits}_s);
 | 
						|
 | 
						|
                switch (id) {
 | 
						|
 | 
						|
EOD;
 | 
						|
 | 
						|
            foreach ($msg["entries"] as $entry) {
 | 
						|
                if (!($entry["type"]["type"] == "uint" && $entry["type"]["size"] == $bits)) {
 | 
						|
                    continue;
 | 
						|
                }
 | 
						|
                $type = make_type_name($msg, $entry);
 | 
						|
                echo <<<EOD
 | 
						|
                    case {$entry["id"]}:
 | 
						|
                        if (o->{$entry["name"]}_start == o->buf_len) {
 | 
						|
                            o->{$entry["name"]}_start = entry_pos;
 | 
						|
                        }
 | 
						|
                        o->{$entry["name"]}_span = pos - o->{$entry["name"]}_start;
 | 
						|
                        {$entry["name"]}_count++;
 | 
						|
                        break;
 | 
						|
 | 
						|
EOD;
 | 
						|
            }
 | 
						|
 | 
						|
            echo <<<EOD
 | 
						|
                    default:
 | 
						|
                        return 0;
 | 
						|
                }
 | 
						|
            } break;
 | 
						|
 | 
						|
EOD;
 | 
						|
        }
 | 
						|
 | 
						|
        echo <<<EOD
 | 
						|
            case BPROTO_TYPE_DATA:
 | 
						|
            case BPROTO_TYPE_CONSTDATA:
 | 
						|
            {
 | 
						|
                if (!(left >= sizeof(struct BProto_data_header_s))) {
 | 
						|
                    return 0;
 | 
						|
                }
 | 
						|
                struct BProto_data_header_s val;
 | 
						|
                memcpy(&val, o->buf + pos, sizeof(val));
 | 
						|
                pos += sizeof(struct BProto_data_header_s);
 | 
						|
                left -= sizeof(struct BProto_data_header_s);
 | 
						|
 | 
						|
                uint32_t payload_len = ltoh32(val.len);
 | 
						|
                if (!(left >= payload_len)) {
 | 
						|
                    return 0;
 | 
						|
                }
 | 
						|
                pos += payload_len;
 | 
						|
                left -= payload_len;
 | 
						|
 | 
						|
                switch (id) {
 | 
						|
 | 
						|
EOD;
 | 
						|
 | 
						|
        foreach ($msg["entries"] as $entry) {
 | 
						|
            if (!in_array($entry["type"]["type"], array("data", "constdata"))) {
 | 
						|
                continue;
 | 
						|
            }
 | 
						|
            $type = make_type_name($msg, $entry);
 | 
						|
            echo <<<EOD
 | 
						|
                    case {$entry["id"]}:
 | 
						|
                        if (!(type == {$type})) {
 | 
						|
                            return 0;
 | 
						|
                        }
 | 
						|
 | 
						|
EOD;
 | 
						|
            if ($entry["type"]["type"] == "constdata") {
 | 
						|
                echo <<<EOD
 | 
						|
                        if (!(payload_len == ({$entry["type"]["size"]}))) {
 | 
						|
                            return 0;
 | 
						|
                        }
 | 
						|
 | 
						|
EOD;
 | 
						|
            }
 | 
						|
            echo <<<EOD
 | 
						|
                        if (o->{$entry["name"]}_start == o->buf_len) {
 | 
						|
                            o->{$entry["name"]}_start = entry_pos;
 | 
						|
                        }
 | 
						|
                        o->{$entry["name"]}_span = pos - o->{$entry["name"]}_start;
 | 
						|
                        {$entry["name"]}_count++;
 | 
						|
                        break;
 | 
						|
 | 
						|
EOD;
 | 
						|
        }
 | 
						|
 | 
						|
        echo <<<EOD
 | 
						|
                    default:
 | 
						|
                        return 0;
 | 
						|
                }
 | 
						|
            } break;
 | 
						|
            default:
 | 
						|
                return 0;
 | 
						|
        }
 | 
						|
    }
 | 
						|
 | 
						|
 | 
						|
EOD;
 | 
						|
 | 
						|
        foreach ($msg["entries"] as $entry) {
 | 
						|
            $cond = "";
 | 
						|
            switch ($entry["cardinality"]) {
 | 
						|
                case "repeated":
 | 
						|
                    break;
 | 
						|
                case "required repeated":
 | 
						|
                    $cond = "{$entry["name"]}_count >= 1";
 | 
						|
                    break;
 | 
						|
                case "optional":
 | 
						|
                    $cond = "{$entry["name"]}_count <= 1";
 | 
						|
                    break;
 | 
						|
                case "required":
 | 
						|
                    $cond = "{$entry["name"]}_count == 1";
 | 
						|
                    break;
 | 
						|
                default:
 | 
						|
                    assert(0);
 | 
						|
            }
 | 
						|
            if ($cond) {
 | 
						|
                echo <<<EOD
 | 
						|
    if (!({$cond})) {
 | 
						|
        return 0;
 | 
						|
    }
 | 
						|
 | 
						|
EOD;
 | 
						|
            }
 | 
						|
        }
 | 
						|
 | 
						|
        echo <<<EOD
 | 
						|
 | 
						|
    return 1;
 | 
						|
}
 | 
						|
 | 
						|
int {$msg["name"]}Parser_GotEverything ({$msg["name"]}Parser *o)
 | 
						|
{
 | 
						|
    return (
 | 
						|
 | 
						|
EOD;
 | 
						|
 | 
						|
        $first = 1;
 | 
						|
        foreach ($msg["entries"] as $entry) {
 | 
						|
            if ($first) {
 | 
						|
                $first = 0;
 | 
						|
            } else {
 | 
						|
                echo <<<EOD
 | 
						|
        &&
 | 
						|
 | 
						|
EOD;
 | 
						|
            }
 | 
						|
            echo <<<EOD
 | 
						|
        o->{$entry["name"]}_pos == o->{$entry["name"]}_span
 | 
						|
 | 
						|
EOD;
 | 
						|
        }
 | 
						|
    
 | 
						|
 | 
						|
        echo <<<EOD
 | 
						|
    );
 | 
						|
}
 | 
						|
 | 
						|
 | 
						|
EOD;
 | 
						|
 | 
						|
        foreach ($msg["entries"] as $entry) {
 | 
						|
            $decl = make_parser_decl($msg, $entry);
 | 
						|
            $reset_decl = make_parser_reset_decl($msg, $entry);
 | 
						|
            $forward_decl = make_parser_forward_decl($msg, $entry);
 | 
						|
            $type = make_type_name($msg, $entry);
 | 
						|
 | 
						|
            echo <<<EOD
 | 
						|
{$decl}
 | 
						|
{
 | 
						|
    ASSERT(o->{$entry["name"]}_pos >= 0)
 | 
						|
    ASSERT(o->{$entry["name"]}_pos <= o->{$entry["name"]}_span)
 | 
						|
 | 
						|
    int left = o->{$entry["name"]}_span - o->{$entry["name"]}_pos;
 | 
						|
 | 
						|
    while (left > 0) {
 | 
						|
        ASSERT(left >= sizeof(struct BProto_header_s))
 | 
						|
        struct BProto_header_s header;
 | 
						|
        memcpy(&header, o->buf + o->{$entry["name"]}_start + o->{$entry["name"]}_pos, sizeof(header));
 | 
						|
        o->{$entry["name"]}_pos += sizeof(struct BProto_header_s);
 | 
						|
        left -= sizeof(struct BProto_header_s);
 | 
						|
        uint16_t type = ltoh16(header.type);
 | 
						|
        uint16_t id = ltoh16(header.id);
 | 
						|
 | 
						|
        switch (type) {
 | 
						|
 | 
						|
EOD;
 | 
						|
 | 
						|
            foreach (array(8, 16, 32, 64) as $bits) {
 | 
						|
                echo <<<EOD
 | 
						|
            case BPROTO_TYPE_UINT{$bits}: {
 | 
						|
                ASSERT(left >= sizeof(struct BProto_uint{$bits}_s))
 | 
						|
 | 
						|
EOD;
 | 
						|
                if ($entry["type"]["type"] == "uint" && $entry["type"]["size"] == $bits) {
 | 
						|
                    echo <<<EOD
 | 
						|
                struct BProto_uint{$bits}_s val;
 | 
						|
                memcpy(&val, o->buf + o->{$entry["name"]}_start + o->{$entry["name"]}_pos, sizeof(val));
 | 
						|
 | 
						|
EOD;
 | 
						|
                }
 | 
						|
                echo <<<EOD
 | 
						|
                o->{$entry["name"]}_pos += sizeof(struct BProto_uint{$bits}_s);
 | 
						|
                left -= sizeof(struct BProto_uint{$bits}_s);
 | 
						|
 | 
						|
EOD;
 | 
						|
                if ($entry["type"]["type"] == "uint" && $entry["type"]["size"] == $bits) {
 | 
						|
                    echo <<<EOD
 | 
						|
 | 
						|
                if (id == {$entry["id"]}) {
 | 
						|
                    *v = ltoh{$bits}(val.v);
 | 
						|
                    return 1;
 | 
						|
                }
 | 
						|
 | 
						|
EOD;
 | 
						|
                }
 | 
						|
 | 
						|
                echo <<<EOD
 | 
						|
            } break;
 | 
						|
 | 
						|
EOD;
 | 
						|
            }
 | 
						|
 | 
						|
            echo <<<EOD
 | 
						|
            case BPROTO_TYPE_DATA:
 | 
						|
            case BPROTO_TYPE_CONSTDATA:
 | 
						|
            {
 | 
						|
                ASSERT(left >= sizeof(struct BProto_data_header_s))
 | 
						|
                struct BProto_data_header_s val;
 | 
						|
                memcpy(&val, o->buf + o->{$entry["name"]}_start + o->{$entry["name"]}_pos, sizeof(val));
 | 
						|
                o->{$entry["name"]}_pos += sizeof(struct BProto_data_header_s);
 | 
						|
                left -= sizeof(struct BProto_data_header_s);
 | 
						|
 | 
						|
                uint32_t payload_len = ltoh32(val.len);
 | 
						|
                ASSERT(left >= payload_len)
 | 
						|
 | 
						|
EOD;
 | 
						|
            if ($entry["type"]["type"] == "data" || $entry["type"]["type"] == "constdata") {
 | 
						|
                    echo <<<EOD
 | 
						|
                uint8_t *payload = o->buf + o->{$entry["name"]}_start + o->{$entry["name"]}_pos;
 | 
						|
 | 
						|
EOD;
 | 
						|
            }
 | 
						|
            echo <<<EOD
 | 
						|
                o->{$entry["name"]}_pos += payload_len;
 | 
						|
                left -= payload_len;
 | 
						|
 | 
						|
EOD;
 | 
						|
            if ($entry["type"]["type"] == "data") {
 | 
						|
                echo <<<EOD
 | 
						|
 | 
						|
                if (type == BPROTO_TYPE_DATA && id == {$entry["id"]}) {
 | 
						|
                    *data = payload;
 | 
						|
                    *data_len = payload_len;
 | 
						|
                    return 1;
 | 
						|
                }
 | 
						|
 | 
						|
EOD;
 | 
						|
            }
 | 
						|
            else if ($entry["type"]["type"] == "constdata") {
 | 
						|
                echo <<<EOD
 | 
						|
 | 
						|
                if (type == BPROTO_TYPE_CONSTDATA && id == {$entry["id"]}) {
 | 
						|
                    *data = payload;
 | 
						|
                    return 1;
 | 
						|
                }
 | 
						|
 | 
						|
EOD;
 | 
						|
            }
 | 
						|
 | 
						|
            echo <<<EOD
 | 
						|
            } break;
 | 
						|
            default:
 | 
						|
                ASSERT(0);
 | 
						|
        }
 | 
						|
    }
 | 
						|
 | 
						|
    return 0;
 | 
						|
}
 | 
						|
 | 
						|
{$reset_decl}
 | 
						|
{
 | 
						|
    o->{$entry["name"]}_pos = 0;
 | 
						|
}
 | 
						|
 | 
						|
{$forward_decl}
 | 
						|
{
 | 
						|
    o->{$entry["name"]}_pos = o->{$entry["name"]}_span;
 | 
						|
}
 | 
						|
 | 
						|
 | 
						|
EOD;
 | 
						|
        }
 | 
						|
    }
 | 
						|
 | 
						|
    return ob_get_clean();
 | 
						|
}
 |