tor-android/external/badvpn_dns/bproto_generator/bproto_functions.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();
}