bproto_functions.php 20 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763
  1. <?php
  2. function tokenize ($str, &$out) {
  3. $out = array();
  4. while (strlen($str) > 0) {
  5. if (preg_match('/^\\/\\/.*/', $str, $matches)) {
  6. $str = substr($str, strlen($matches[0]));
  7. }
  8. else if (preg_match('/^\\s+/', $str, $matches)) {
  9. $str = substr($str, strlen($matches[0]));
  10. }
  11. else if (preg_match('/^include/', $str, $matches)) {
  12. $out[] = array('include', null);
  13. $str = substr($str, strlen($matches[0]));
  14. }
  15. else if (preg_match('/^message/', $str, $matches)) {
  16. $out[] = array('message', null);
  17. $str = substr($str, strlen($matches[0]));
  18. }
  19. else if (preg_match('/^repeated/', $str, $matches)) {
  20. $out[] = array('repeated', null);
  21. $str = substr($str, strlen($matches[0]));
  22. }
  23. else if (preg_match('/^required/', $str, $matches)) {
  24. $out[] = array('required', null);
  25. $str = substr($str, strlen($matches[0]));
  26. }
  27. else if (preg_match('/^optional/', $str, $matches)) {
  28. $out[] = array('optional', null);
  29. $str = substr($str, strlen($matches[0]));
  30. }
  31. else if (preg_match('/^{/', $str, $matches)) {
  32. $out[] = array('spar', null);
  33. $str = substr($str, strlen($matches[0]));
  34. }
  35. else if (preg_match('/^}/', $str, $matches)) {
  36. $out[] = array('epar', null);
  37. $str = substr($str, strlen($matches[0]));
  38. }
  39. else if (preg_match('/^\(/', $str, $matches)) {
  40. $out[] = array('srpar', null);
  41. $str = substr($str, strlen($matches[0]));
  42. }
  43. else if (preg_match('/^\)/', $str, $matches)) {
  44. $out[] = array('erpar', null);
  45. $str = substr($str, strlen($matches[0]));
  46. }
  47. else if (preg_match('/^=/', $str, $matches)) {
  48. $out[] = array('equals', null);
  49. $str = substr($str, strlen($matches[0]));
  50. }
  51. else if (preg_match('/^;/', $str, $matches)) {
  52. $out[] = array('semicolon', null);
  53. $str = substr($str, strlen($matches[0]));
  54. }
  55. else if (preg_match('/^uint(8|16|32|64)/', $str, $matches)) {
  56. $out[] = array('uint', $matches[1]);
  57. $str = substr($str, strlen($matches[0]));
  58. }
  59. else if (preg_match('/^data/', $str, $matches)) {
  60. $out[] = array('data', null);
  61. $str = substr($str, strlen($matches[0]));
  62. }
  63. else if (preg_match('/^[0-9]+/', $str, $matches)) {
  64. $out[] = array('number', $matches[0]);
  65. $str = substr($str, strlen($matches[0]));
  66. }
  67. else if (preg_match('/^[a-zA-Z_][a-zA-Z0-9_]*/', $str, $matches)) {
  68. $out[] = array('name', $matches[0]);
  69. $str = substr($str, strlen($matches[0]));
  70. }
  71. else if (preg_match('/^"([^"]*)"/', $str, $matches)) {
  72. $out[] = array('string', $matches[1]);
  73. $str = substr($str, strlen($matches[0]));
  74. }
  75. else {
  76. return FALSE;
  77. }
  78. }
  79. return TRUE;
  80. }
  81. function fatal_error ($message)
  82. {
  83. fwrite(STDERR, "Fatal error: $message\n");
  84. ob_get_clean();
  85. exit(1);
  86. }
  87. function make_writer_decl ($msg, $entry)
  88. {
  89. switch ($entry["type"]["type"]) {
  90. case "uint":
  91. return "void {$msg["name"]}Writer_Add{$entry["name"]} ({$msg["name"]}Writer *o, uint{$entry["type"]["size"]}_t v)";
  92. case "data":
  93. return "uint8_t * {$msg["name"]}Writer_Add{$entry["name"]} ({$msg["name"]}Writer *o, int len)";
  94. case "constdata":
  95. return "uint8_t * {$msg["name"]}Writer_Add{$entry["name"]} ({$msg["name"]}Writer *o)";
  96. default:
  97. assert(0);
  98. }
  99. }
  100. function make_parser_decl ($msg, $entry)
  101. {
  102. switch ($entry["type"]["type"]) {
  103. case "uint":
  104. return "int {$msg["name"]}Parser_Get{$entry["name"]} ({$msg["name"]}Parser *o, uint{$entry["type"]["size"]}_t *v)";
  105. case "data":
  106. return "int {$msg["name"]}Parser_Get{$entry["name"]} ({$msg["name"]}Parser *o, uint8_t **data, int *data_len)";
  107. case "constdata":
  108. return "int {$msg["name"]}Parser_Get{$entry["name"]} ({$msg["name"]}Parser *o, uint8_t **data)";
  109. default:
  110. assert(0);
  111. }
  112. }
  113. function make_parser_reset_decl ($msg, $entry)
  114. {
  115. return "void {$msg["name"]}Parser_Reset{$entry["name"]} ({$msg["name"]}Parser *o)";
  116. }
  117. function make_parser_forward_decl ($msg, $entry)
  118. {
  119. return "void {$msg["name"]}Parser_Forward{$entry["name"]} ({$msg["name"]}Parser *o)";
  120. }
  121. function make_type_name ($msg, $entry)
  122. {
  123. switch ($entry["type"]["type"]) {
  124. case "uint":
  125. return "BPROTO_TYPE_UINT{$entry["type"]["size"]}";
  126. case "data":
  127. return "BPROTO_TYPE_DATA";
  128. case "constdata":
  129. return "BPROTO_TYPE_CONSTDATA";
  130. default:
  131. assert(0);
  132. }
  133. }
  134. function make_finish_assert ($msg, $entry)
  135. {
  136. switch ($entry["cardinality"]) {
  137. case "repeated":
  138. return "ASSERT(o->{$entry["name"]}_count >= 0)";
  139. case "required repeated":
  140. return "ASSERT(o->{$entry["name"]}_count >= 1)";
  141. case "optional":
  142. return "ASSERT(o->{$entry["name"]}_count >= 0 && o->{$entry["name"]}_count <= 1)";
  143. case "required":
  144. return "ASSERT(o->{$entry["name"]}_count == 1)";
  145. default:
  146. assert(0);
  147. }
  148. }
  149. function make_add_count_assert ($msg, $entry)
  150. {
  151. if (in_array($entry["cardinality"], array("optional", "required"))) {
  152. return "ASSERT(o->{$entry["name"]}_count == 0)";
  153. }
  154. return "";
  155. }
  156. function make_add_length_assert ($msg, $entry)
  157. {
  158. if ($entry["type"]["type"] == "data") {
  159. return "ASSERT(len >= 0 && len <= UINT32_MAX)";
  160. }
  161. return "";
  162. }
  163. function make_size_define ($msg, $entry)
  164. {
  165. switch ($entry["type"]["type"]) {
  166. case "uint":
  167. return "#define {$msg["name"]}_SIZE{$entry["name"]} (sizeof(struct BProto_header_s) + sizeof(struct BProto_uint{$entry["type"]["size"]}_s))";
  168. case "data":
  169. return "#define {$msg["name"]}_SIZE{$entry["name"]}(_len) (sizeof(struct BProto_header_s) + sizeof(struct BProto_data_header_s) + (_len))";
  170. case "constdata":
  171. return "#define {$msg["name"]}_SIZE{$entry["name"]} (sizeof(struct BProto_header_s) + sizeof(struct BProto_data_header_s) + ({$entry["type"]["size"]}))";
  172. default:
  173. assert(0);
  174. }
  175. }
  176. function generate_header ($name, $directives, $messages) {
  177. ob_start();
  178. echo <<<EOD
  179. /*
  180. DO NOT EDIT THIS FILE!
  181. This file was automatically generated by the bproto generator.
  182. */
  183. #include <stdint.h>
  184. #include <misc/debug.h>
  185. #include <misc/byteorder.h>
  186. #include <bproto/BProto.h>
  187. EOD;
  188. foreach ($directives as $directive) {
  189. if ($directive["type"] == "include") {
  190. echo <<<EOD
  191. #include "{$directive["file"]}"
  192. EOD;
  193. }
  194. }
  195. echo <<<EOD
  196. EOD;
  197. foreach ($messages as $msg) {
  198. foreach ($msg["entries"] as $entry) {
  199. $def = make_size_define($msg, $entry);
  200. echo <<<EOD
  201. {$def}
  202. EOD;
  203. }
  204. echo <<<EOD
  205. typedef struct {
  206. uint8_t *out;
  207. int used;
  208. EOD;
  209. foreach ($msg["entries"] as $entry) {
  210. echo <<<EOD
  211. int {$entry["name"]}_count;
  212. EOD;
  213. }
  214. echo <<<EOD
  215. } {$msg["name"]}Writer;
  216. static void {$msg["name"]}Writer_Init ({$msg["name"]}Writer *o, uint8_t *out);
  217. static int {$msg["name"]}Writer_Finish ({$msg["name"]}Writer *o);
  218. EOD;
  219. foreach ($msg["entries"] as $entry) {
  220. $decl = make_writer_decl($msg, $entry);
  221. echo <<<EOD
  222. static {$decl};
  223. EOD;
  224. }
  225. echo <<<EOD
  226. typedef struct {
  227. uint8_t *buf;
  228. int buf_len;
  229. EOD;
  230. foreach ($msg["entries"] as $entry) {
  231. echo <<<EOD
  232. int {$entry["name"]}_start;
  233. int {$entry["name"]}_span;
  234. int {$entry["name"]}_pos;
  235. EOD;
  236. }
  237. echo <<<EOD
  238. } {$msg["name"]}Parser;
  239. static int {$msg["name"]}Parser_Init ({$msg["name"]}Parser *o, uint8_t *buf, int buf_len);
  240. static int {$msg["name"]}Parser_GotEverything ({$msg["name"]}Parser *o);
  241. EOD;
  242. foreach ($msg["entries"] as $entry) {
  243. $decl = make_parser_decl($msg, $entry);
  244. $reset_decl = make_parser_reset_decl($msg, $entry);
  245. $forward_decl = make_parser_forward_decl($msg, $entry);
  246. echo <<<EOD
  247. static {$decl};
  248. static {$reset_decl};
  249. static {$forward_decl};
  250. EOD;
  251. }
  252. echo <<<EOD
  253. void {$msg["name"]}Writer_Init ({$msg["name"]}Writer *o, uint8_t *out)
  254. {
  255. o->out = out;
  256. o->used = 0;
  257. EOD;
  258. foreach ($msg["entries"] as $entry) {
  259. echo <<<EOD
  260. o->{$entry["name"]}_count = 0;
  261. EOD;
  262. }
  263. echo <<<EOD
  264. }
  265. int {$msg["name"]}Writer_Finish ({$msg["name"]}Writer *o)
  266. {
  267. ASSERT(o->used >= 0)
  268. EOD;
  269. foreach ($msg["entries"] as $entry) {
  270. $ass = make_finish_assert($msg, $entry);
  271. echo <<<EOD
  272. {$ass}
  273. EOD;
  274. }
  275. echo <<<EOD
  276. return o->used;
  277. }
  278. EOD;
  279. foreach ($msg["entries"] as $entry) {
  280. $decl = make_writer_decl($msg, $entry);
  281. $type = make_type_name($msg, $entry);
  282. $add_count_assert = make_add_count_assert($msg, $entry);
  283. $add_length_assert = make_add_length_assert($msg, $entry);
  284. echo <<<EOD
  285. {$decl}
  286. {
  287. ASSERT(o->used >= 0)
  288. {$add_count_assert}
  289. {$add_length_assert}
  290. ((struct BProto_header_s *)(o->out + o->used))->id = htol16({$entry["id"]});
  291. ((struct BProto_header_s *)(o->out + o->used))->type = htol16({$type});
  292. o->used += sizeof(struct BProto_header_s);
  293. EOD;
  294. switch ($entry["type"]["type"]) {
  295. case "uint":
  296. echo <<<EOD
  297. ((struct BProto_uint{$entry["type"]["size"]}_s *)(o->out + o->used))->v = htol{$entry["type"]["size"]}(v);
  298. o->used += sizeof(struct BProto_uint{$entry["type"]["size"]}_s);
  299. EOD;
  300. break;
  301. case "data":
  302. echo <<<EOD
  303. ((struct BProto_data_header_s *)(o->out + o->used))->len = htol32(len);
  304. o->used += sizeof(struct BProto_data_header_s);
  305. uint8_t *dest = (o->out + o->used);
  306. o->used += len;
  307. EOD;
  308. break;
  309. case "constdata":
  310. echo <<<EOD
  311. ((struct BProto_data_header_s *)(o->out + o->used))->len = htol32({$entry["type"]["size"]});
  312. o->used += sizeof(struct BProto_data_header_s);
  313. uint8_t *dest = (o->out + o->used);
  314. o->used += ({$entry["type"]["size"]});
  315. EOD;
  316. break;
  317. default:
  318. assert(0);
  319. }
  320. echo <<<EOD
  321. o->{$entry["name"]}_count++;
  322. EOD;
  323. if (in_array($entry["type"]["type"], array("data", "constdata"))) {
  324. echo <<<EOD
  325. return dest;
  326. EOD;
  327. }
  328. echo <<<EOD
  329. }
  330. EOD;
  331. }
  332. echo <<<EOD
  333. int {$msg["name"]}Parser_Init ({$msg["name"]}Parser *o, uint8_t *buf, int buf_len)
  334. {
  335. ASSERT(buf_len >= 0)
  336. o->buf = buf;
  337. o->buf_len = buf_len;
  338. EOD;
  339. foreach ($msg["entries"] as $entry) {
  340. echo <<<EOD
  341. o->{$entry["name"]}_start = o->buf_len;
  342. o->{$entry["name"]}_span = 0;
  343. o->{$entry["name"]}_pos = 0;
  344. EOD;
  345. }
  346. echo <<<EOD
  347. EOD;
  348. foreach ($msg["entries"] as $entry) {
  349. echo <<<EOD
  350. int {$entry["name"]}_count = 0;
  351. EOD;
  352. }
  353. echo <<<EOD
  354. int pos = 0;
  355. int left = o->buf_len;
  356. while (left > 0) {
  357. int entry_pos = pos;
  358. if (!(left >= sizeof(struct BProto_header_s))) {
  359. return 0;
  360. }
  361. struct BProto_header_s *header = (struct BProto_header_s *)(o->buf + pos);
  362. pos += sizeof(struct BProto_header_s);
  363. left -= sizeof(struct BProto_header_s);
  364. uint16_t type = ltoh16(header->type);
  365. uint16_t id = ltoh16(header->id);
  366. switch (type) {
  367. EOD;
  368. foreach (array(8, 16, 32, 64) as $bits) {
  369. echo <<<EOD
  370. case BPROTO_TYPE_UINT{$bits}: {
  371. if (!(left >= sizeof(struct BProto_uint{$bits}_s))) {
  372. return 0;
  373. }
  374. pos += sizeof(struct BProto_uint{$bits}_s);
  375. left -= sizeof(struct BProto_uint{$bits}_s);
  376. switch (id) {
  377. EOD;
  378. foreach ($msg["entries"] as $entry) {
  379. if (!($entry["type"]["type"] == "uint" && $entry["type"]["size"] == $bits)) {
  380. continue;
  381. }
  382. $type = make_type_name($msg, $entry);
  383. echo <<<EOD
  384. case {$entry["id"]}:
  385. if (o->{$entry["name"]}_start == o->buf_len) {
  386. o->{$entry["name"]}_start = entry_pos;
  387. }
  388. o->{$entry["name"]}_span = pos - o->{$entry["name"]}_start;
  389. {$entry["name"]}_count++;
  390. break;
  391. EOD;
  392. }
  393. echo <<<EOD
  394. default:
  395. return 0;
  396. }
  397. } break;
  398. EOD;
  399. }
  400. echo <<<EOD
  401. case BPROTO_TYPE_DATA:
  402. case BPROTO_TYPE_CONSTDATA:
  403. {
  404. if (!(left >= sizeof(struct BProto_data_header_s))) {
  405. return 0;
  406. }
  407. struct BProto_data_header_s *val = (struct BProto_data_header_s *)(o->buf + pos);
  408. pos += sizeof(struct BProto_data_header_s);
  409. left -= sizeof(struct BProto_data_header_s);
  410. uint32_t payload_len = ltoh32(val->len);
  411. if (!(left >= payload_len)) {
  412. return 0;
  413. }
  414. pos += payload_len;
  415. left -= payload_len;
  416. switch (id) {
  417. EOD;
  418. foreach ($msg["entries"] as $entry) {
  419. if (!in_array($entry["type"]["type"], array("data", "constdata"))) {
  420. continue;
  421. }
  422. $type = make_type_name($msg, $entry);
  423. echo <<<EOD
  424. case {$entry["id"]}:
  425. if (!(type == {$type})) {
  426. return 0;
  427. }
  428. EOD;
  429. if ($entry["type"]["type"] == "constdata") {
  430. echo <<<EOD
  431. if (!(payload_len == ({$entry["type"]["size"]}))) {
  432. return 0;
  433. }
  434. EOD;
  435. }
  436. echo <<<EOD
  437. if (o->{$entry["name"]}_start == o->buf_len) {
  438. o->{$entry["name"]}_start = entry_pos;
  439. }
  440. o->{$entry["name"]}_span = pos - o->{$entry["name"]}_start;
  441. {$entry["name"]}_count++;
  442. break;
  443. EOD;
  444. }
  445. echo <<<EOD
  446. default:
  447. return 0;
  448. }
  449. } break;
  450. default:
  451. return 0;
  452. }
  453. }
  454. EOD;
  455. foreach ($msg["entries"] as $entry) {
  456. $cond = "";
  457. switch ($entry["cardinality"]) {
  458. case "repeated":
  459. break;
  460. case "required repeated":
  461. $cond = "{$entry["name"]}_count >= 1";
  462. break;
  463. case "optional":
  464. $cond = "{$entry["name"]}_count <= 1";
  465. break;
  466. case "required":
  467. $cond = "{$entry["name"]}_count == 1";
  468. break;
  469. default:
  470. assert(0);
  471. }
  472. if ($cond) {
  473. echo <<<EOD
  474. if (!({$cond})) {
  475. return 0;
  476. }
  477. EOD;
  478. }
  479. }
  480. echo <<<EOD
  481. return 1;
  482. }
  483. int {$msg["name"]}Parser_GotEverything ({$msg["name"]}Parser *o)
  484. {
  485. return (
  486. EOD;
  487. $first = 1;
  488. foreach ($msg["entries"] as $entry) {
  489. if ($first) {
  490. $first = 0;
  491. } else {
  492. echo <<<EOD
  493. &&
  494. EOD;
  495. }
  496. echo <<<EOD
  497. o->{$entry["name"]}_pos == o->{$entry["name"]}_span
  498. EOD;
  499. }
  500. echo <<<EOD
  501. );
  502. }
  503. EOD;
  504. foreach ($msg["entries"] as $entry) {
  505. $decl = make_parser_decl($msg, $entry);
  506. $reset_decl = make_parser_reset_decl($msg, $entry);
  507. $forward_decl = make_parser_forward_decl($msg, $entry);
  508. $type = make_type_name($msg, $entry);
  509. echo <<<EOD
  510. {$decl}
  511. {
  512. ASSERT(o->{$entry["name"]}_pos >= 0)
  513. ASSERT(o->{$entry["name"]}_pos <= o->{$entry["name"]}_span)
  514. int left = o->{$entry["name"]}_span - o->{$entry["name"]}_pos;
  515. while (left > 0) {
  516. ASSERT(left >= sizeof(struct BProto_header_s))
  517. struct BProto_header_s *header = (struct BProto_header_s *)(o->buf + o->{$entry["name"]}_start + o->{$entry["name"]}_pos);
  518. o->{$entry["name"]}_pos += sizeof(struct BProto_header_s);
  519. left -= sizeof(struct BProto_header_s);
  520. uint16_t type = ltoh16(header->type);
  521. uint16_t id = ltoh16(header->id);
  522. switch (type) {
  523. EOD;
  524. foreach (array(8, 16, 32, 64) as $bits) {
  525. echo <<<EOD
  526. case BPROTO_TYPE_UINT{$bits}: {
  527. ASSERT(left >= sizeof(struct BProto_uint{$bits}_s))
  528. EOD;
  529. if ($entry["type"]["type"] == "uint" && $entry["type"]["size"] == $bits) {
  530. echo <<<EOD
  531. struct BProto_uint{$bits}_s *val = (struct BProto_uint{$bits}_s *)(o->buf + o->{$entry["name"]}_start + o->{$entry["name"]}_pos);
  532. EOD;
  533. }
  534. echo <<<EOD
  535. o->{$entry["name"]}_pos += sizeof(struct BProto_uint{$bits}_s);
  536. left -= sizeof(struct BProto_uint{$bits}_s);
  537. EOD;
  538. if ($entry["type"]["type"] == "uint" && $entry["type"]["size"] == $bits) {
  539. echo <<<EOD
  540. if (id == {$entry["id"]}) {
  541. *v = ltoh{$bits}(val->v);
  542. return 1;
  543. }
  544. EOD;
  545. }
  546. echo <<<EOD
  547. } break;
  548. EOD;
  549. }
  550. echo <<<EOD
  551. case BPROTO_TYPE_DATA:
  552. case BPROTO_TYPE_CONSTDATA:
  553. {
  554. ASSERT(left >= sizeof(struct BProto_data_header_s))
  555. struct BProto_data_header_s *val = (struct BProto_data_header_s *)(o->buf + o->{$entry["name"]}_start + o->{$entry["name"]}_pos);
  556. o->{$entry["name"]}_pos += sizeof(struct BProto_data_header_s);
  557. left -= sizeof(struct BProto_data_header_s);
  558. uint32_t payload_len = ltoh32(val->len);
  559. ASSERT(left >= payload_len)
  560. EOD;
  561. if ($entry["type"]["type"] == "data" || $entry["type"]["type"] == "constdata") {
  562. echo <<<EOD
  563. uint8_t *payload = o->buf + o->{$entry["name"]}_start + o->{$entry["name"]}_pos;
  564. EOD;
  565. }
  566. echo <<<EOD
  567. o->{$entry["name"]}_pos += payload_len;
  568. left -= payload_len;
  569. EOD;
  570. if ($entry["type"]["type"] == "data") {
  571. echo <<<EOD
  572. if (type == BPROTO_TYPE_DATA && id == {$entry["id"]}) {
  573. *data = payload;
  574. *data_len = payload_len;
  575. return 1;
  576. }
  577. EOD;
  578. }
  579. else if ($entry["type"]["type"] == "constdata") {
  580. echo <<<EOD
  581. if (type == BPROTO_TYPE_CONSTDATA && id == {$entry["id"]}) {
  582. *data = payload;
  583. return 1;
  584. }
  585. EOD;
  586. }
  587. echo <<<EOD
  588. } break;
  589. default:
  590. ASSERT(0);
  591. }
  592. }
  593. return 0;
  594. }
  595. {$reset_decl}
  596. {
  597. o->{$entry["name"]}_pos = 0;
  598. }
  599. {$forward_decl}
  600. {
  601. o->{$entry["name"]}_pos = o->{$entry["name"]}_span;
  602. }
  603. EOD;
  604. }
  605. }
  606. return ob_get_clean();
  607. }