bproto_functions.php 19 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748
  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. struct BProto_uint{$bits}_s *val = (struct BProto_uint{$bits}_s *)(o->buf + pos);
  375. pos += sizeof(struct BProto_uint{$bits}_s);
  376. left -= sizeof(struct BProto_uint{$bits}_s);
  377. switch (id) {
  378. EOD;
  379. foreach ($msg["entries"] as $entry) {
  380. if (!($entry["type"]["type"] == "uint" && $entry["type"]["size"] == $bits)) {
  381. continue;
  382. }
  383. $type = make_type_name($msg, $entry);
  384. echo <<<EOD
  385. case {$entry["id"]}:
  386. if (o->{$entry["name"]}_start == o->buf_len) {
  387. o->{$entry["name"]}_start = entry_pos;
  388. }
  389. o->{$entry["name"]}_span = pos - o->{$entry["name"]}_start;
  390. {$entry["name"]}_count++;
  391. break;
  392. EOD;
  393. }
  394. echo <<<EOD
  395. default:
  396. return 0;
  397. }
  398. } break;
  399. EOD;
  400. }
  401. echo <<<EOD
  402. case BPROTO_TYPE_DATA:
  403. case BPROTO_TYPE_CONSTDATA:
  404. {
  405. if (!(left >= sizeof(struct BProto_data_header_s))) {
  406. return 0;
  407. }
  408. struct BProto_data_header_s *val = (struct BProto_data_header_s *)(o->buf + pos);
  409. pos += sizeof(struct BProto_data_header_s);
  410. left -= sizeof(struct BProto_data_header_s);
  411. int payload_len = ltoh32(val->len);
  412. if (!(left >= payload_len)) {
  413. return 0;
  414. }
  415. pos += payload_len;
  416. left -= payload_len;
  417. switch (id) {
  418. EOD;
  419. foreach ($msg["entries"] as $entry) {
  420. if (!in_array($entry["type"]["type"], array("data", "constdata"))) {
  421. continue;
  422. }
  423. $type = make_type_name($msg, $entry);
  424. echo <<<EOD
  425. case {$entry["id"]}:
  426. if (!(type == {$type})) {
  427. return 0;
  428. }
  429. EOD;
  430. if ($entry["type"]["type"] == "constdata") {
  431. echo <<<EOD
  432. if (!(payload_len == ({$entry["type"]["size"]}))) {
  433. return 0;
  434. }
  435. EOD;
  436. }
  437. echo <<<EOD
  438. if (o->{$entry["name"]}_start == o->buf_len) {
  439. o->{$entry["name"]}_start = entry_pos;
  440. }
  441. o->{$entry["name"]}_span = pos - o->{$entry["name"]}_start;
  442. {$entry["name"]}_count++;
  443. break;
  444. EOD;
  445. }
  446. echo <<<EOD
  447. default:
  448. return 0;
  449. }
  450. } break;
  451. default:
  452. return 0;
  453. }
  454. }
  455. EOD;
  456. foreach ($msg["entries"] as $entry) {
  457. $cond = "";
  458. switch ($entry["cardinality"]) {
  459. case "repeated":
  460. break;
  461. case "required repeated":
  462. $cond = "{$entry["name"]}_count >= 1";
  463. break;
  464. case "optional":
  465. $cond = "{$entry["name"]}_count <= 1";
  466. break;
  467. case "required":
  468. $cond = "{$entry["name"]}_count == 1";
  469. break;
  470. default:
  471. assert(0);
  472. }
  473. if ($cond) {
  474. echo <<<EOD
  475. if (!({$cond})) {
  476. return 0;
  477. }
  478. EOD;
  479. }
  480. }
  481. echo <<<EOD
  482. return 1;
  483. }
  484. int {$msg["name"]}Parser_GotEverything ({$msg["name"]}Parser *o)
  485. {
  486. return (
  487. EOD;
  488. $first = 1;
  489. foreach ($msg["entries"] as $entry) {
  490. if ($first) {
  491. $first = 0;
  492. } else {
  493. echo <<<EOD
  494. &&
  495. EOD;
  496. }
  497. echo <<<EOD
  498. o->{$entry["name"]}_pos == o->{$entry["name"]}_span
  499. EOD;
  500. }
  501. echo <<<EOD
  502. );
  503. }
  504. EOD;
  505. foreach ($msg["entries"] as $entry) {
  506. $decl = make_parser_decl($msg, $entry);
  507. $reset_decl = make_parser_reset_decl($msg, $entry);
  508. $forward_decl = make_parser_forward_decl($msg, $entry);
  509. $type = make_type_name($msg, $entry);
  510. echo <<<EOD
  511. {$decl}
  512. {
  513. ASSERT(o->{$entry["name"]}_pos >= 0)
  514. ASSERT(o->{$entry["name"]}_pos <= o->{$entry["name"]}_span)
  515. int left = o->{$entry["name"]}_span - o->{$entry["name"]}_pos;
  516. while (left > 0) {
  517. ASSERT(left >= sizeof(struct BProto_header_s))
  518. struct BProto_header_s *header = (struct BProto_header_s *)(o->buf + o->{$entry["name"]}_start + o->{$entry["name"]}_pos);
  519. o->{$entry["name"]}_pos += sizeof(struct BProto_header_s);
  520. left -= sizeof(struct BProto_header_s);
  521. uint16_t type = ltoh16(header->type);
  522. uint16_t id = ltoh16(header->id);
  523. switch (type) {
  524. EOD;
  525. foreach (array(8, 16, 32, 64) as $bits) {
  526. echo <<<EOD
  527. case BPROTO_TYPE_UINT{$bits}: {
  528. ASSERT(left >= sizeof(struct BProto_uint{$bits}_s))
  529. struct BProto_uint{$bits}_s *val = (struct BProto_uint{$bits}_s *)(o->buf + o->{$entry["name"]}_start + o->{$entry["name"]}_pos);
  530. o->{$entry["name"]}_pos += sizeof(struct BProto_uint{$bits}_s);
  531. left -= sizeof(struct BProto_uint{$bits}_s);
  532. EOD;
  533. if ($entry["type"]["type"] == "uint" && $entry["type"]["size"] == $bits) {
  534. echo <<<EOD
  535. if (id == {$entry["id"]}) {
  536. *v = ltoh{$bits}(val->v);
  537. return 1;
  538. }
  539. EOD;
  540. }
  541. echo <<<EOD
  542. } break;
  543. EOD;
  544. }
  545. echo <<<EOD
  546. case BPROTO_TYPE_DATA:
  547. case BPROTO_TYPE_CONSTDATA:
  548. {
  549. ASSERT(left >= sizeof(struct BProto_data_header_s))
  550. struct BProto_data_header_s *val = (struct BProto_data_header_s *)(o->buf + o->{$entry["name"]}_start + o->{$entry["name"]}_pos);
  551. o->{$entry["name"]}_pos += sizeof(struct BProto_data_header_s);
  552. left -= sizeof(struct BProto_data_header_s);
  553. int payload_len = ltoh32(val->len);
  554. ASSERT(left >= payload_len)
  555. uint8_t *payload = o->buf + o->{$entry["name"]}_start + o->{$entry["name"]}_pos;
  556. o->{$entry["name"]}_pos += payload_len;
  557. left -= payload_len;
  558. EOD;
  559. if ($entry["type"]["type"] == "data") {
  560. echo <<<EOD
  561. if (type == BPROTO_TYPE_DATA && id == {$entry["id"]}) {
  562. *data = payload;
  563. *data_len = payload_len;
  564. return 1;
  565. }
  566. EOD;
  567. }
  568. else if ($entry["type"]["type"] == "constdata") {
  569. echo <<<EOD
  570. if (type == BPROTO_TYPE_CONSTDATA && id == {$entry["id"]}) {
  571. *data = payload;
  572. return 1;
  573. }
  574. EOD;
  575. }
  576. echo <<<EOD
  577. } break;
  578. default:
  579. ASSERT(0);
  580. }
  581. }
  582. return 0;
  583. }
  584. {$reset_decl}
  585. {
  586. o->{$entry["name"]}_pos = 0;
  587. }
  588. {$forward_decl}
  589. {
  590. o->{$entry["name"]}_pos = o->{$entry["name"]}_span;
  591. }
  592. EOD;
  593. }
  594. }
  595. return ob_get_clean();
  596. }