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 << #include #include #include #include EOD; foreach ($directives as $directive) { if ($directive["type"] == "include") { echo <<out = out; o->used = 0; EOD; foreach ($msg["entries"] as $entry) { echo <<{$entry["name"]}_count = 0; EOD; } echo <<used >= 0) EOD; foreach ($msg["entries"] as $entry) { $ass = make_finish_assert($msg, $entry); echo <<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 <<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 <<out + o->used, &data, sizeof(data)); o->used += sizeof(struct BProto_uint{$entry["type"]["size"]}_s); EOD; break; case "data": echo <<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 <<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 <<{$entry["name"]}_count++; EOD; if (in_array($entry["type"]["type"], array("data", "constdata"))) { echo <<= 0) o->buf = buf; o->buf_len = buf_len; EOD; foreach ($msg["entries"] as $entry) { echo <<{$entry["name"]}_start = o->buf_len; o->{$entry["name"]}_span = 0; o->{$entry["name"]}_pos = 0; EOD; } echo <<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 <<= 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 <<{$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 <<= 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 <<{$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 <<= 1"; break; case "optional": $cond = "{$entry["name"]}_count <= 1"; break; case "required": $cond = "{$entry["name"]}_count == 1"; break; default: assert(0); } if ($cond) { echo <<{$entry["name"]}_pos == o->{$entry["name"]}_span EOD; } echo <<{$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 <<= sizeof(struct BProto_uint{$bits}_s)) EOD; if ($entry["type"]["type"] == "uint" && $entry["type"]["size"] == $bits) { echo <<buf + o->{$entry["name"]}_start + o->{$entry["name"]}_pos, sizeof(val)); EOD; } echo <<{$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 <<= 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 <<buf + o->{$entry["name"]}_start + o->{$entry["name"]}_pos; EOD; } echo <<{$entry["name"]}_pos += payload_len; left -= payload_len; EOD; if ($entry["type"]["type"] == "data") { echo <<{$entry["name"]}_pos = 0; } {$forward_decl} { o->{$entry["name"]}_pos = o->{$entry["name"]}_span; } EOD; } } return ob_get_clean(); }