├── .gitignore ├── CHANGES ├── LICENSE ├── README.md ├── alberto.descr ├── alberto.opam ├── dune ├── dune-project ├── examples ├── dune ├── echo.erl ├── port_lwt.ml └── port_simple.ml ├── src ├── META ├── alberto.ml ├── alberto.mldylib ├── alberto.mli ├── alberto.mllib └── dune └── tests.ml /.gitignore: -------------------------------------------------------------------------------- 1 | _build 2 | _dep 3 | setup.data 4 | setup.log 5 | *.ba* 6 | *.native 7 | -------------------------------------------------------------------------------- /CHANGES: -------------------------------------------------------------------------------- 1 | alberto Changelog 2 | ================= 3 | 4 | Here you can see the full list of changes between each alberto release. 5 | 6 | Version 0.5 7 | ----------- 8 | 9 | Maintenance release, released on November 21st, 2017 10 | 11 | - Removed dependency on Camlp4 12 | - Switched from ocamlbuild and OASIS to jbuilder 13 | 14 | Version 0.4 15 | ----------- 16 | 17 | Bugfix release, released on March 3rd, 2015 18 | 19 | - Fixed an off-by-one error introduced after migration to 'safe_string' 20 | - Disabled warnings-as-errors 21 | 22 | Version 0.3 23 | ----------- 24 | 25 | Bugfix release, relased on December 23rd, 2014 26 | 27 | - Eliminated warnings produced by OCaml 4.02. 28 | 29 | Version 0.2 30 | ----------- 31 | 32 | Bugfix release, released on May 13th, 2014 33 | 34 | - Switched to 'ocplib-endian' for serialization of integral types. 35 | 36 | 37 | Version 0.1 38 | ----------- 39 | 40 | Initial release, released on October 25th, 2011 41 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | =============================================================================== 2 | 3 | OCAML SPECIAL EXCEPTION 4 | 5 | As a special exception to the GNU Library General Public License, you may link, 6 | statically or dynamically, a "work that uses the Library" with a publicly 7 | distributed version of the Library to produce an executable file containing 8 | portions of the Library, and distribute that executable file under terms of 9 | your choice, without any of the additional requirements listed in clause 6 of 10 | the GNU Library General Public License. By "a publicly distributed version of 11 | the Library", we mean either the unmodified Library as distributed by the 12 | original author, or a modified version of the Library that is distributed under 13 | the conditions defined in clause 3 of the GNU Library General Public License. 14 | This exception does not however invalidate any other reasons why the executable 15 | file might be covered by the GNU Library General Public License. 16 | 17 | =============================================================================== 18 | 19 | GNU LESSER GENERAL PUBLIC LICENSE 20 | Version 2.1, February 1999 21 | 22 | Copyright (C) 1991, 1999 Free Software Foundation, Inc. 23 | 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA 24 | Everyone is permitted to copy and distribute verbatim copies 25 | of this license document, but changing it is not allowed. 26 | 27 | [This is the first released version of the Lesser GPL. It also counts 28 | as the successor of the GNU Library Public License, version 2, hence 29 | the version number 2.1.] 30 | 31 | Preamble 32 | 33 | The licenses for most software are designed to take away your 34 | freedom to share and change it. By contrast, the GNU General Public 35 | Licenses are intended to guarantee your freedom to share and change 36 | free software--to make sure the software is free for all its users. 37 | 38 | This license, the Lesser General Public License, applies to some 39 | specially designated software packages--typically libraries--of the 40 | Free Software Foundation and other authors who decide to use it. You 41 | can use it too, but we suggest you first think carefully about whether 42 | this license or the ordinary General Public License is the better 43 | strategy to use in any particular case, based on the explanations below. 44 | 45 | When we speak of free software, we are referring to freedom of use, 46 | not price. Our General Public Licenses are designed to make sure that 47 | you have the freedom to distribute copies of free software (and charge 48 | for this service if you wish); that you receive source code or can get 49 | it if you want it; that you can change the software and use pieces of 50 | it in new free programs; and that you are informed that you can do 51 | these things. 52 | 53 | To protect your rights, we need to make restrictions that forbid 54 | distributors to deny you these rights or to ask you to surrender these 55 | rights. These restrictions translate to certain responsibilities for 56 | you if you distribute copies of the library or if you modify it. 57 | 58 | For example, if you distribute copies of the library, whether gratis 59 | or for a fee, you must give the recipients all the rights that we gave 60 | you. You must make sure that they, too, receive or can get the source 61 | code. If you link other code with the library, you must provide 62 | complete object files to the recipients, so that they can relink them 63 | with the library after making changes to the library and recompiling 64 | it. And you must show them these terms so they know their rights. 65 | 66 | We protect your rights with a two-step method: (1) we copyright the 67 | library, and (2) we offer you this license, which gives you legal 68 | permission to copy, distribute and/or modify the library. 69 | 70 | To protect each distributor, we want to make it very clear that 71 | there is no warranty for the free library. Also, if the library is 72 | modified by someone else and passed on, the recipients should know 73 | that what they have is not the original version, so that the original 74 | author's reputation will not be affected by problems that might be 75 | introduced by others. 76 | 77 | Finally, software patents pose a constant threat to the existence of 78 | any free program. We wish to make sure that a company cannot 79 | effectively restrict the users of a free program by obtaining a 80 | restrictive license from a patent holder. Therefore, we insist that 81 | any patent license obtained for a version of the library must be 82 | consistent with the full freedom of use specified in this license. 83 | 84 | Most GNU software, including some libraries, is covered by the 85 | ordinary GNU General Public License. This license, the GNU Lesser 86 | General Public License, applies to certain designated libraries, and 87 | is quite different from the ordinary General Public License. We use 88 | this license for certain libraries in order to permit linking those 89 | libraries into non-free programs. 90 | 91 | When a program is linked with a library, whether statically or using 92 | a shared library, the combination of the two is legally speaking a 93 | combined work, a derivative of the original library. The ordinary 94 | General Public License therefore permits such linking only if the 95 | entire combination fits its criteria of freedom. The Lesser General 96 | Public License permits more lax criteria for linking other code with 97 | the library. 98 | 99 | We call this license the "Lesser" General Public License because it 100 | does Less to protect the user's freedom than the ordinary General 101 | Public License. It also provides other free software developers Less 102 | of an advantage over competing non-free programs. These disadvantages 103 | are the reason we use the ordinary General Public License for many 104 | libraries. However, the Lesser license provides advantages in certain 105 | special circumstances. 106 | 107 | For example, on rare occasions, there may be a special need to 108 | encourage the widest possible use of a certain library, so that it becomes 109 | a de-facto standard. To achieve this, non-free programs must be 110 | allowed to use the library. A more frequent case is that a free 111 | library does the same job as widely used non-free libraries. In this 112 | case, there is little to gain by limiting the free library to free 113 | software only, so we use the Lesser General Public License. 114 | 115 | In other cases, permission to use a particular library in non-free 116 | programs enables a greater number of people to use a large body of 117 | free software. For example, permission to use the GNU C Library in 118 | non-free programs enables many more people to use the whole GNU 119 | operating system, as well as its variant, the GNU/Linux operating 120 | system. 121 | 122 | Although the Lesser General Public License is Less protective of the 123 | users' freedom, it does ensure that the user of a program that is 124 | linked with the Library has the freedom and the wherewithal to run 125 | that program using a modified version of the Library. 126 | 127 | The precise terms and conditions for copying, distribution and 128 | modification follow. Pay close attention to the difference between a 129 | "work based on the library" and a "work that uses the library". The 130 | former contains code derived from the library, whereas the latter must 131 | be combined with the library in order to run. 132 | 133 | GNU LESSER GENERAL PUBLIC LICENSE 134 | TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION 135 | 136 | 0. This License Agreement applies to any software library or other 137 | program which contains a notice placed by the copyright holder or 138 | other authorized party saying it may be distributed under the terms of 139 | this Lesser General Public License (also called "this License"). 140 | Each licensee is addressed as "you". 141 | 142 | A "library" means a collection of software functions and/or data 143 | prepared so as to be conveniently linked with application programs 144 | (which use some of those functions and data) to form executables. 145 | 146 | The "Library", below, refers to any such software library or work 147 | which has been distributed under these terms. A "work based on the 148 | Library" means either the Library or any derivative work under 149 | copyright law: that is to say, a work containing the Library or a 150 | portion of it, either verbatim or with modifications and/or translated 151 | straightforwardly into another language. (Hereinafter, translation is 152 | included without limitation in the term "modification".) 153 | 154 | "Source code" for a work means the preferred form of the work for 155 | making modifications to it. For a library, complete source code means 156 | all the source code for all modules it contains, plus any associated 157 | interface definition files, plus the scripts used to control compilation 158 | and installation of the library. 159 | 160 | Activities other than copying, distribution and modification are not 161 | covered by this License; they are outside its scope. The act of 162 | running a program using the Library is not restricted, and output from 163 | such a program is covered only if its contents constitute a work based 164 | on the Library (independent of the use of the Library in a tool for 165 | writing it). Whether that is true depends on what the Library does 166 | and what the program that uses the Library does. 167 | 168 | 1. You may copy and distribute verbatim copies of the Library's 169 | complete source code as you receive it, in any medium, provided that 170 | you conspicuously and appropriately publish on each copy an 171 | appropriate copyright notice and disclaimer of warranty; keep intact 172 | all the notices that refer to this License and to the absence of any 173 | warranty; and distribute a copy of this License along with the 174 | Library. 175 | 176 | You may charge a fee for the physical act of transferring a copy, 177 | and you may at your option offer warranty protection in exchange for a 178 | fee. 179 | 180 | 2. You may modify your copy or copies of the Library or any portion 181 | of it, thus forming a work based on the Library, and copy and 182 | distribute such modifications or work under the terms of Section 1 183 | above, provided that you also meet all of these conditions: 184 | 185 | a) The modified work must itself be a software library. 186 | 187 | b) You must cause the files modified to carry prominent notices 188 | stating that you changed the files and the date of any change. 189 | 190 | c) You must cause the whole of the work to be licensed at no 191 | charge to all third parties under the terms of this License. 192 | 193 | d) If a facility in the modified Library refers to a function or a 194 | table of data to be supplied by an application program that uses 195 | the facility, other than as an argument passed when the facility 196 | is invoked, then you must make a good faith effort to ensure that, 197 | in the event an application does not supply such function or 198 | table, the facility still operates, and performs whatever part of 199 | its purpose remains meaningful. 200 | 201 | (For example, a function in a library to compute square roots has 202 | a purpose that is entirely well-defined independent of the 203 | application. Therefore, Subsection 2d requires that any 204 | application-supplied function or table used by this function must 205 | be optional: if the application does not supply it, the square 206 | root function must still compute square roots.) 207 | 208 | These requirements apply to the modified work as a whole. If 209 | identifiable sections of that work are not derived from the Library, 210 | and can be reasonably considered independent and separate works in 211 | themselves, then this License, and its terms, do not apply to those 212 | sections when you distribute them as separate works. But when you 213 | distribute the same sections as part of a whole which is a work based 214 | on the Library, the distribution of the whole must be on the terms of 215 | this License, whose permissions for other licensees extend to the 216 | entire whole, and thus to each and every part regardless of who wrote 217 | it. 218 | 219 | Thus, it is not the intent of this section to claim rights or contest 220 | your rights to work written entirely by you; rather, the intent is to 221 | exercise the right to control the distribution of derivative or 222 | collective works based on the Library. 223 | 224 | In addition, mere aggregation of another work not based on the Library 225 | with the Library (or with a work based on the Library) on a volume of 226 | a storage or distribution medium does not bring the other work under 227 | the scope of this License. 228 | 229 | 3. You may opt to apply the terms of the ordinary GNU General Public 230 | License instead of this License to a given copy of the Library. To do 231 | this, you must alter all the notices that refer to this License, so 232 | that they refer to the ordinary GNU General Public License, version 2, 233 | instead of to this License. (If a newer version than version 2 of the 234 | ordinary GNU General Public License has appeared, then you can specify 235 | that version instead if you wish.) Do not make any other change in 236 | these notices. 237 | 238 | Once this change is made in a given copy, it is irreversible for 239 | that copy, so the ordinary GNU General Public License applies to all 240 | subsequent copies and derivative works made from that copy. 241 | 242 | This option is useful when you wish to copy part of the code of 243 | the Library into a program that is not a library. 244 | 245 | 4. You may copy and distribute the Library (or a portion or 246 | derivative of it, under Section 2) in object code or executable form 247 | under the terms of Sections 1 and 2 above provided that you accompany 248 | it with the complete corresponding machine-readable source code, which 249 | must be distributed under the terms of Sections 1 and 2 above on a 250 | medium customarily used for software interchange. 251 | 252 | If distribution of object code is made by offering access to copy 253 | from a designated place, then offering equivalent access to copy the 254 | source code from the same place satisfies the requirement to 255 | distribute the source code, even though third parties are not 256 | compelled to copy the source along with the object code. 257 | 258 | 5. A program that contains no derivative of any portion of the 259 | Library, but is designed to work with the Library by being compiled or 260 | linked with it, is called a "work that uses the Library". Such a 261 | work, in isolation, is not a derivative work of the Library, and 262 | therefore falls outside the scope of this License. 263 | 264 | However, linking a "work that uses the Library" with the Library 265 | creates an executable that is a derivative of the Library (because it 266 | contains portions of the Library), rather than a "work that uses the 267 | library". The executable is therefore covered by this License. 268 | Section 6 states terms for distribution of such executables. 269 | 270 | When a "work that uses the Library" uses material from a header file 271 | that is part of the Library, the object code for the work may be a 272 | derivative work of the Library even though the source code is not. 273 | Whether this is true is especially significant if the work can be 274 | linked without the Library, or if the work is itself a library. The 275 | threshold for this to be true is not precisely defined by law. 276 | 277 | If such an object file uses only numerical parameters, data 278 | structure layouts and accessors, and small macros and small inline 279 | functions (ten lines or less in length), then the use of the object 280 | file is unrestricted, regardless of whether it is legally a derivative 281 | work. (Executables containing this object code plus portions of the 282 | Library will still fall under Section 6.) 283 | 284 | Otherwise, if the work is a derivative of the Library, you may 285 | distribute the object code for the work under the terms of Section 6. 286 | Any executables containing that work also fall under Section 6, 287 | whether or not they are linked directly with the Library itself. 288 | 289 | 6. As an exception to the Sections above, you may also combine or 290 | link a "work that uses the Library" with the Library to produce a 291 | work containing portions of the Library, and distribute that work 292 | under terms of your choice, provided that the terms permit 293 | modification of the work for the customer's own use and reverse 294 | engineering for debugging such modifications. 295 | 296 | You must give prominent notice with each copy of the work that the 297 | Library is used in it and that the Library and its use are covered by 298 | this License. You must supply a copy of this License. If the work 299 | during execution displays copyright notices, you must include the 300 | copyright notice for the Library among them, as well as a reference 301 | directing the user to the copy of this License. Also, you must do one 302 | of these things: 303 | 304 | a) Accompany the work with the complete corresponding 305 | machine-readable source code for the Library including whatever 306 | changes were used in the work (which must be distributed under 307 | Sections 1 and 2 above); and, if the work is an executable linked 308 | with the Library, with the complete machine-readable "work that 309 | uses the Library", as object code and/or source code, so that the 310 | user can modify the Library and then relink to produce a modified 311 | executable containing the modified Library. (It is understood 312 | that the user who changes the contents of definitions files in the 313 | Library will not necessarily be able to recompile the application 314 | to use the modified definitions.) 315 | 316 | b) Use a suitable shared library mechanism for linking with the 317 | Library. A suitable mechanism is one that (1) uses at run time a 318 | copy of the library already present on the user's computer system, 319 | rather than copying library functions into the executable, and (2) 320 | will operate properly with a modified version of the library, if 321 | the user installs one, as long as the modified version is 322 | interface-compatible with the version that the work was made with. 323 | 324 | c) Accompany the work with a written offer, valid for at 325 | least three years, to give the same user the materials 326 | specified in Subsection 6a, above, for a charge no more 327 | than the cost of performing this distribution. 328 | 329 | d) If distribution of the work is made by offering access to copy 330 | from a designated place, offer equivalent access to copy the above 331 | specified materials from the same place. 332 | 333 | e) Verify that the user has already received a copy of these 334 | materials or that you have already sent this user a copy. 335 | 336 | For an executable, the required form of the "work that uses the 337 | Library" must include any data and utility programs needed for 338 | reproducing the executable from it. However, as a special exception, 339 | the materials to be distributed need not include anything that is 340 | normally distributed (in either source or binary form) with the major 341 | components (compiler, kernel, and so on) of the operating system on 342 | which the executable runs, unless that component itself accompanies 343 | the executable. 344 | 345 | It may happen that this requirement contradicts the license 346 | restrictions of other proprietary libraries that do not normally 347 | accompany the operating system. Such a contradiction means you cannot 348 | use both them and the Library together in an executable that you 349 | distribute. 350 | 351 | 7. You may place library facilities that are a work based on the 352 | Library side-by-side in a single library together with other library 353 | facilities not covered by this License, and distribute such a combined 354 | library, provided that the separate distribution of the work based on 355 | the Library and of the other library facilities is otherwise 356 | permitted, and provided that you do these two things: 357 | 358 | a) Accompany the combined library with a copy of the same work 359 | based on the Library, uncombined with any other library 360 | facilities. This must be distributed under the terms of the 361 | Sections above. 362 | 363 | b) Give prominent notice with the combined library of the fact 364 | that part of it is a work based on the Library, and explaining 365 | where to find the accompanying uncombined form of the same work. 366 | 367 | 8. You may not copy, modify, sublicense, link with, or distribute 368 | the Library except as expressly provided under this License. Any 369 | attempt otherwise to copy, modify, sublicense, link with, or 370 | distribute the Library is void, and will automatically terminate your 371 | rights under this License. However, parties who have received copies, 372 | or rights, from you under this License will not have their licenses 373 | terminated so long as such parties remain in full compliance. 374 | 375 | 9. You are not required to accept this License, since you have not 376 | signed it. However, nothing else grants you permission to modify or 377 | distribute the Library or its derivative works. These actions are 378 | prohibited by law if you do not accept this License. Therefore, by 379 | modifying or distributing the Library (or any work based on the 380 | Library), you indicate your acceptance of this License to do so, and 381 | all its terms and conditions for copying, distributing or modifying 382 | the Library or works based on it. 383 | 384 | 10. Each time you redistribute the Library (or any work based on the 385 | Library), the recipient automatically receives a license from the 386 | original licensor to copy, distribute, link with or modify the Library 387 | subject to these terms and conditions. You may not impose any further 388 | restrictions on the recipients' exercise of the rights granted herein. 389 | You are not responsible for enforcing compliance by third parties with 390 | this License. 391 | 392 | 11. If, as a consequence of a court judgment or allegation of patent 393 | infringement or for any other reason (not limited to patent issues), 394 | conditions are imposed on you (whether by court order, agreement or 395 | otherwise) that contradict the conditions of this License, they do not 396 | excuse you from the conditions of this License. If you cannot 397 | distribute so as to satisfy simultaneously your obligations under this 398 | License and any other pertinent obligations, then as a consequence you 399 | may not distribute the Library at all. For example, if a patent 400 | license would not permit royalty-free redistribution of the Library by 401 | all those who receive copies directly or indirectly through you, then 402 | the only way you could satisfy both it and this License would be to 403 | refrain entirely from distribution of the Library. 404 | 405 | If any portion of this section is held invalid or unenforceable under any 406 | particular circumstance, the balance of the section is intended to apply, 407 | and the section as a whole is intended to apply in other circumstances. 408 | 409 | It is not the purpose of this section to induce you to infringe any 410 | patents or other property right claims or to contest validity of any 411 | such claims; this section has the sole purpose of protecting the 412 | integrity of the free software distribution system which is 413 | implemented by public license practices. Many people have made 414 | generous contributions to the wide range of software distributed 415 | through that system in reliance on consistent application of that 416 | system; it is up to the author/donor to decide if he or she is willing 417 | to distribute software through any other system and a licensee cannot 418 | impose that choice. 419 | 420 | This section is intended to make thoroughly clear what is believed to 421 | be a consequence of the rest of this License. 422 | 423 | 12. If the distribution and/or use of the Library is restricted in 424 | certain countries either by patents or by copyrighted interfaces, the 425 | original copyright holder who places the Library under this License may add 426 | an explicit geographical distribution limitation excluding those countries, 427 | so that distribution is permitted only in or among countries not thus 428 | excluded. In such case, this License incorporates the limitation as if 429 | written in the body of this License. 430 | 431 | 13. The Free Software Foundation may publish revised and/or new 432 | versions of the Lesser General Public License from time to time. 433 | Such new versions will be similar in spirit to the present version, 434 | but may differ in detail to address new problems or concerns. 435 | 436 | Each version is given a distinguishing version number. If the Library 437 | specifies a version number of this License which applies to it and 438 | "any later version", you have the option of following the terms and 439 | conditions either of that version or of any later version published by 440 | the Free Software Foundation. If the Library does not specify a 441 | license version number, you may choose any version ever published by 442 | the Free Software Foundation. 443 | 444 | 14. If you wish to incorporate parts of the Library into other free 445 | programs whose distribution conditions are incompatible with these, 446 | write to the author to ask for permission. For software which is 447 | copyrighted by the Free Software Foundation, write to the Free 448 | Software Foundation; we sometimes make exceptions for this. Our 449 | decision will be guided by the two goals of preserving the free status 450 | of all derivatives of our free software and of promoting the sharing 451 | and reuse of software generally. 452 | 453 | NO WARRANTY 454 | 455 | 15. BECAUSE THE LIBRARY IS LICENSED FREE OF CHARGE, THERE IS NO 456 | WARRANTY FOR THE LIBRARY, TO THE EXTENT PERMITTED BY APPLICABLE LAW. 457 | EXCEPT WHEN OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR 458 | OTHER PARTIES PROVIDE THE LIBRARY "AS IS" WITHOUT WARRANTY OF ANY 459 | KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE 460 | IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR 461 | PURPOSE. THE ENTIRE RISK AS TO THE QUALITY AND PERFORMANCE OF THE 462 | LIBRARY IS WITH YOU. SHOULD THE LIBRARY PROVE DEFECTIVE, YOU ASSUME 463 | THE COST OF ALL NECESSARY SERVICING, REPAIR OR CORRECTION. 464 | 465 | 16. IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN 466 | WRITING WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MAY MODIFY 467 | AND/OR REDISTRIBUTE THE LIBRARY AS PERMITTED ABOVE, BE LIABLE TO YOU 468 | FOR DAMAGES, INCLUDING ANY GENERAL, SPECIAL, INCIDENTAL OR 469 | CONSEQUENTIAL DAMAGES ARISING OUT OF THE USE OR INABILITY TO USE THE 470 | LIBRARY (INCLUDING BUT NOT LIMITED TO LOSS OF DATA OR DATA BEING 471 | RENDERED INACCURATE OR LOSSES SUSTAINED BY YOU OR THIRD PARTIES OR A 472 | FAILURE OF THE LIBRARY TO OPERATE WITH ANY OTHER SOFTWARE), EVEN IF 473 | SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH 474 | DAMAGES. 475 | 476 | END OF TERMS AND CONDITIONS 477 | 478 | How to Apply These Terms to Your New Libraries 479 | 480 | If you develop a new library, and you want it to be of the greatest 481 | possible use to the public, we recommend making it free software that 482 | everyone can redistribute and change. You can do so by permitting 483 | redistribution under these terms (or, alternatively, under the terms of the 484 | ordinary General Public License). 485 | 486 | To apply these terms, attach the following notices to the library. It is 487 | safest to attach them to the start of each source file to most effectively 488 | convey the exclusion of warranty; and each file should have at least the 489 | "copyright" line and a pointer to where the full notice is found. 490 | 491 | 492 | Copyright (C) 493 | 494 | This library is free software; you can redistribute it and/or 495 | modify it under the terms of the GNU Lesser General Public 496 | License as published by the Free Software Foundation; either 497 | version 2.1 of the License, or (at your option) any later version. 498 | 499 | This library is distributed in the hope that it will be useful, 500 | but WITHOUT ANY WARRANTY; without even the implied warranty of 501 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU 502 | Lesser General Public License for more details. 503 | 504 | You should have received a copy of the GNU Lesser General Public 505 | License along with this library; if not, write to the Free Software 506 | Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA 507 | 508 | Also add information on how to contact you by electronic and paper mail. 509 | 510 | You should also get your employer (if you work as a programmer) or your 511 | school, if any, to sign a "copyright disclaimer" for the library, if 512 | necessary. Here is a sample; alter the names: 513 | 514 | Yoyodyne, Inc., hereby disclaims all copyright interest in the 515 | library `Frob' (a library for tweaking knobs) written by James Random Hacker. 516 | 517 | , 1 April 1990 518 | Ty Coon, President of Vice 519 | 520 | That's all there is to it! 521 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | ``` 2 | __ 3 | / \ 4 | / ..|\ 5 | (_\ |_) 6 | / \@' Alberto! 7 | / \ 0.5 8 | _ _/ ` | 9 | \\/ \ | _\ 10 | \ /_ || \\_ 11 | \____)|_) \_) 12 | ``` 13 | 14 | Alberto is an implementation of 15 | [Erlang External Term Format](http://erlang.org/doc/apps/erts/erl_ext_dist.html), 16 | a protocol, used by Erlang nodes to communicate with so called *ports*. See 17 | Erlang [documentation](http://www.erlang.org/doc/tutorial/c_port.html) for 18 | details. 19 | 20 | Installation 21 | ------------ 22 | 23 | The simplest way to get `alberto` is to use [`OPAM`](http://opam.ocamlpro.com): 24 | 25 | ```bash 26 | opam install alberto 27 | ``` 28 | 29 | Example 30 | ------- 31 | 32 | On Erlang part we have a simple echo server, which sends `<<"Hello world!">>` 33 | to the port and echoes whatever comes back to stdout: 34 | 35 | ```erlang 36 | #!/usr/bin/env escript 37 | %%! -noshell -noinput 38 | -mode(compile). 39 | -define(ME, filename:basename(escript:script_name())). 40 | -define(PRINT(STR, PARAMS), io:format("~s: " ++ STR ++ "~n", [?ME | PARAMS])). 41 | 42 | 43 | main([Bin]) -> 44 | Port = open_port({spawn, Bin}, 45 | [binary, {packet, 4}]), 46 | port_command(Port, term_to_binary("Hello world!")), 47 | receive 48 | {_, {data, Data}} -> 49 | ?PRINT("~p", [binary_to_term(Data)]), 50 | main([Bin]) 51 | end; 52 | main(_) -> 53 | io:format("~s", ["usage: echo.erl path/to/port"]). 54 | ``` 55 | 56 | OCaml part is even more consice: 57 | 58 | ```ocaml 59 | Alberto.interact (fun x -> x) 60 | ``` 61 | 62 | Okay, let's see it in action (sources are available in `examples/` directory): 63 | 64 | ```bash 65 | $ dune build examples/port_simple.exe 66 | $ examples/echo.erl ./_build/default/examples/port_simple.exe 67 | echo.erl: "Hello world!" 68 | echo.erl: "Hello world!" 69 | echo.erl: "Hello world!" 70 | echo.erl: "Hello world!" 71 | ``` 72 | -------------------------------------------------------------------------------- /alberto.descr: -------------------------------------------------------------------------------- 1 | OCaml interface to Erlang ports 2 | 3 | Alberto is an implementation of 4 | [Erlang External Term Format](http://erlang.org/doc/apps/erts/erl_ext_dist.html), 5 | a protocol, used by Erlang nodes to communicate with so called *ports*. See 6 | Erlang [documentation](http://www.erlang.org/doc/tutorial/c_port.html) for 7 | details. 8 | -------------------------------------------------------------------------------- /alberto.opam: -------------------------------------------------------------------------------- 1 | opam-version: "2.0" 2 | build: [ 3 | ["dune" "build" "-p" name "-j" jobs] 4 | ] 5 | depends: [ 6 | "ocplib-endian" 7 | "dune" {build & >= "1.0"} 8 | "ocaml" {>= "4.01"} 9 | ] 10 | 11 | author: "Sergei Lebedev" 12 | maintainer: "superbobry@gmail.com" 13 | homepage: "https://github.com/selectel/alberto" 14 | bug-reports: "https://github.com/selectel/alberto/issues" 15 | dev-repo: "git+https://github.com/selectel/alberto" 16 | doc: "https://selectel.github.io/alberto/" 17 | license: "LGPL-2.1+ with OCaml linking exception" 18 | descr: """ 19 | OCaml interface to Erlang ports 20 | 21 | Alberto is an implementation of 22 | [Erlang External Term Format](http://erlang.org/doc/apps/erts/erl_ext_dist.html), 23 | a protocol, used by Erlang nodes to communicate with so called *ports*. See 24 | Erlang [documentation](http://www.erlang.org/doc/tutorial/c_port.html) for 25 | details. 26 | """ 27 | -------------------------------------------------------------------------------- /dune: -------------------------------------------------------------------------------- 1 | (executable 2 | (name tests) 3 | (libraries alberto ocplib-endian num bytes kaputt kaputt.num)) 4 | 5 | (alias 6 | (name runtest) 7 | (deps 8 | (:< tests.exe)) 9 | (action 10 | (run %{<}))) 11 | -------------------------------------------------------------------------------- /dune-project: -------------------------------------------------------------------------------- 1 | (lang dune 1.0) 2 | (name alberto) 3 | -------------------------------------------------------------------------------- /examples/dune: -------------------------------------------------------------------------------- 1 | (executable 2 | (name port_simple) 3 | (modules port_simple) 4 | (libraries alberto ocplib-endian)) 5 | 6 | (executable 7 | (name port_lwt) 8 | (modules port_lwt) 9 | (libraries alberto ocplib-endian bytes lwt.unix)) 10 | -------------------------------------------------------------------------------- /examples/echo.erl: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env escript 2 | %%! -noshell -noinput 3 | -mode(compile). 4 | -define(ME, filename:basename(escript:script_name())). 5 | -define(PRINT(STR, PARAMS), io:format("~s: " ++ STR ++ "~n", [?ME | PARAMS])). 6 | 7 | loop(Port) -> 8 | port_command(Port, term_to_binary({"Hello world!", erlang:now()})), 9 | receive 10 | {_, {data, Data}} -> 11 | ?PRINT("~p", [binary_to_term(Data)]), 12 | loop(Port) 13 | end. 14 | 15 | main([Bin]) -> 16 | Port = open_port({spawn, Bin}, 17 | [binary, {packet, 4}]), 18 | loop(Port); 19 | main(_) -> 20 | io:format("~s", ["usage: echo.erl path/to/port"]). -------------------------------------------------------------------------------- /examples/port_lwt.ml: -------------------------------------------------------------------------------- 1 | (** Erlang ports, the Lwt way! *) 2 | 3 | module Alberto_lwt : sig 4 | val read_term : Lwt_io.input_channel -> Alberto.t Lwt.t 5 | (** [read_term ic] fetches an Erlang [!term] from the port channel [ic]. *) 6 | 7 | val write_term : Lwt_io.output_channel -> Alberto.t -> unit Lwt.t 8 | (** [write_term oc term] serializes a given Erlang [!term] to the port 9 | channel [oc]. *) 10 | 11 | val interact : (Alberto.t -> Alberto.t) -> unit Lwt.t 12 | (** [interact f] main port loop, recieves Erlang terms from stdin, 13 | executes a given key function [f] and sends back the results. *) 14 | end = struct 15 | open Lwt 16 | 17 | let rec read_term ic = 18 | Lwt_io.BE.read_int ic >>= fun len -> 19 | (* The current version of lwt doesn't use -safe-string. *) 20 | let buf = Bytes.create len in 21 | Lwt_io.read_into_exactly ic buf 0 len >>= fun _ -> 22 | return @@ Alberto.decode_exn (Bytes.to_string buf) 23 | 24 | and write_term oc term = 25 | let buf = Alberto.encode_exn term in 26 | Lwt_io.BE.write_int oc (String.length buf) >>= fun () -> 27 | Lwt_io.write oc buf >>= fun () -> Lwt_io.flush oc 28 | 29 | and interact f = 30 | let rec transform state = function 31 | | `Atom "stop" -> exit 0 32 | | `Atom "ping" -> state, `Atom "pong" 33 | | term -> 34 | let term = try f term with exn -> 35 | `Tuple [`Atom "error"; 36 | `Tuple [term; `String (Printexc.to_string exn)]] 37 | in (state, term) 38 | and loop state = 39 | read_term Lwt_io.stdin >>= fun term -> 40 | let (state, term) = transform state term in 41 | write_term Lwt_io.stdout term >>= fun () -> loop state 42 | in Lwt.catch (fun () -> loop 0) (fun exn -> 43 | match exn with 44 | | End_of_file -> exit 1 45 | | _ -> raise exn) 46 | end 47 | 48 | 49 | let () = Lwt_main.run (Alberto_lwt.interact (fun x -> x)) 50 | -------------------------------------------------------------------------------- /examples/port_simple.ml: -------------------------------------------------------------------------------- 1 | (** A simple echo-server example. *) 2 | 3 | let () = Alberto.interact (fun x -> x) 4 | -------------------------------------------------------------------------------- /src/META: -------------------------------------------------------------------------------- 1 | # OASIS_START 2 | # DO NOT EDIT (digest: ab8f83b1461ed4cf551136330d8b19bf) 3 | version = "0.4" 4 | description = "OCaml interface to Erlang ports" 5 | requires = "ocplib-endian camlp4 num bytes" 6 | archive(byte) = "alberto.cma" 7 | archive(byte, plugin) = "alberto.cma" 8 | archive(native) = "alberto.cmxa" 9 | archive(native, plugin) = "alberto.cmxs" 10 | exists_if = "alberto.cma" 11 | # OASIS_STOP 12 | 13 | -------------------------------------------------------------------------------- /src/alberto.ml: -------------------------------------------------------------------------------- 1 | open Big_int 2 | 3 | type t = [ `Int of int 4 | | `Float of float 5 | | `Atom of string 6 | | `Reference of (string * string * int) 7 | | `Port of (string * int * int) 8 | | `PID of (string * string * int * int) 9 | | `Tuple of t list 10 | | `List of t list 11 | | `String of string 12 | | `Binary of Buffer.t 13 | | `Bignum of big_int 14 | | `NewReference of (string * int * string) 15 | | `BitBinary of (Buffer.t * int) 16 | ] 17 | 18 | (* +-------+ 19 | | Utils | 20 | +-------+ *) 21 | 22 | module BE = EndianString.BigEndian 23 | 24 | let (<| ) f x = f x 25 | 26 | let failwithf fmt = Printf.kprintf failwith fmt 27 | 28 | 29 | (** Shortcuts for some of the binary functions. *) 30 | let pack_byte n = 31 | let buf = Bytes.create 1 in begin 32 | BE.set_int8 buf 0 n; 33 | buf 34 | end 35 | and pack_word n = 36 | let buf = Bytes.create 2 in begin 37 | BE.set_int16 buf 0 n; 38 | buf 39 | end 40 | and pack_int32 n = 41 | let buf = Bytes.create 4 in begin 42 | BE.set_int32 buf 0 (Int32.of_int n); 43 | buf 44 | end 45 | 46 | 47 | (** [big_int_digits n] returns a list of digits of a given number [n]. *) 48 | let big_int_digits n = 49 | let offset = big_int_of_int 256 in 50 | let rec aux n l = 51 | if eq_big_int n zero_big_int then 52 | l 53 | else 54 | let q, m = quomod_big_int n offset in 55 | aux q (int_of_big_int m :: l) 56 | in aux n [] 57 | 58 | 59 | module Stream = struct 60 | include Stream 61 | 62 | (** [take n st] returns a *sub-stream* of stream [st], which has [n] 63 | or fewer characters. *) 64 | let rec take n st = 65 | if n > 0 then 66 | slazy (fun _ -> take (n - 1) st) |> icons (next st) 67 | else 68 | sempty 69 | 70 | (** [to_string ?length st] returns string representation of a given 71 | stream [st], using a buffer of a given [length] for conversion. *) 72 | let to_string ?(length=64) st = 73 | let buf = Buffer.create length in begin 74 | iter (fun c -> Buffer.add_char buf c) st; 75 | Buffer.contents buf 76 | end 77 | 78 | (** [take_string len st] extracts and returns a string of a given [length] 79 | from stream [st]. *) 80 | let take_string length st = take length st |> to_string ~length 81 | 82 | (** [take_byte st] extracts and returns a 1 byte unsigned integer 83 | from stream [st]. *) 84 | let take_byte st = BE.get_uint8 (take_string 1 st) 0 85 | 86 | (** [take_word st] extracts and returns a 2 byte unsigned integer 87 | from stream [st]. *) 88 | let take_word st = BE.get_uint16 (take_string 2 st) 0 89 | 90 | (** [take_int32 st] extracts and returns a 4 byte signed integer from 91 | stream [st]. *) 92 | let take_int32 st = BE.get_int32 (take_string 4 st) 0 |> Int32.to_int 93 | 94 | (** [take_big len st] extracts and returns a [!Bignum] of length 95 | [len] from stream [st]. *) 96 | let take_big len st = 97 | let offset = 256 in 98 | let rec aux idx n = match idx with 99 | | 0 -> n 100 | | _ -> 101 | if idx < 0 then 102 | failwithf "Invalid length: %i" idx 103 | else begin 104 | let digit = take_byte st in 105 | aux (idx - 1) 106 | (add_int_big_int digit (mult_int_big_int offset n)) 107 | end 108 | in aux len (big_int_of_int 0) 109 | 110 | (** [take_list len f st] extracts and returns a [!List] of items 111 | fetched by [f] from stream [st]. *) 112 | let take_list len f st = 113 | let rec aux idx l = match idx with 114 | | 0 -> List.rev l 115 | | _ -> 116 | if idx < 0 then 117 | failwithf "Invalid length: %i" idx 118 | else (f st) :: l |> aux (idx - 1) 119 | in aux len [] 120 | end 121 | 122 | 123 | module String = struct 124 | include String 125 | 126 | let resize s n = 127 | let buf = Bytes.make n '\000' in 128 | 129 | if length s < n then 130 | blit s 0 buf 0 (length s) 131 | else 132 | blit s 0 buf 0 n; 133 | 134 | Bytes.to_string buf 135 | end 136 | 137 | 138 | (* +--------+ 139 | | Parser | 140 | +--------+ *) 141 | 142 | module S = Stream 143 | 144 | let rec parse s = match S.next s with 145 | (* SMALL_INTEGER_EXT: Unsigned 8 bit integer. *) 146 | | '\097' -> `Int (S.take_byte s) 147 | 148 | (* INTEGER_EXT: Signed 32 bit integer in big-endian format, i.e. 149 | MSB first. *) 150 | | '\098' -> `Int (S.take_int32 s) 151 | 152 | (* FLOAT_EXT: A float is stored in string format. the format used 153 | in sprintf to format the float is "%.20e". *) 154 | | '\099' -> 155 | let blob = S.take_string 31 s in 156 | let zeros = String.index blob '\000' in 157 | `Float (float_of_string <| String.sub blob 0 zeros) 158 | 159 | (* ATOM_EXT *) 160 | | '\100' -> 161 | let len = S.take_word s in 162 | if len < 256 163 | then `Atom (S.take_string len s) 164 | else failwithf "ATOM_EXT length exceeded %i > 256" len 165 | 166 | (* REFERENCE_EXT *) 167 | | '\101' -> 168 | let node = parse s in 169 | let id = S.take_string 4 s in 170 | let creation = S.take_byte s in 171 | let node = match node with 172 | | `Atom s -> s 173 | | #t -> failwithf "Unexpected REFERENCE_EXT format" 174 | in `Reference (node, id, creation) 175 | 176 | (* PORT_EXT *) 177 | | '\102' -> 178 | let node = parse s in 179 | let id = S.take_int32 s in 180 | let creation = S.take_byte s in 181 | let node = match node with 182 | | `Atom s -> s 183 | | #t -> failwithf "Unexpected PORT_EXT format" 184 | in `Port (node, id, creation) 185 | 186 | (* PID_EXT *) 187 | | '\103' -> 188 | let node = parse s in 189 | let id = S.take_string 4 s in 190 | let serial = S.take_int32 s in 191 | let creation = S.take_byte s in 192 | let node = match node with 193 | | `Atom s -> s 194 | | #t -> failwithf "Unexpected PID_EXT format" 195 | in `PID (node, id, serial, creation) 196 | 197 | (* SMALL_TUPLE_EXT *) 198 | | '\104' -> 199 | let arity = S.take_byte s in 200 | let elements = S.take_list arity parse s in 201 | `Tuple elements 202 | (* LARGE_TUPLE_EXT *) 203 | | '\105' -> 204 | let arity = S.take_int32 s in 205 | let elements = S.take_list arity parse s in 206 | `Tuple elements 207 | 208 | (* NIL_EXT *) 209 | | '\106' -> `List [] 210 | (* LIST_EXT *) 211 | | '\108' -> 212 | let len = S.take_int32 s in 213 | let elements = S.take_list len parse s in 214 | begin match S.next s with 215 | (* NIL. *) 216 | | '\106' -> `List elements 217 | | _ -> failwith "Unexpected LIST_EXT format" 218 | end 219 | 220 | (* STRING_EXT *) 221 | | '\107' -> 222 | let len = S.take_word s in 223 | let blob = S.take_string len s in 224 | `String blob 225 | 226 | (* BINARY_EXT *) 227 | | '\109' -> 228 | let len = S.take_int32 s in 229 | let blob = S.take_string len s in 230 | let buf = Buffer.create len in begin 231 | Buffer.add_string buf blob; 232 | `Binary buf 233 | end 234 | 235 | (* SMALL_BIG_EXT *) 236 | | '\110' -> 237 | let len = S.take_byte s in 238 | let sign = S.take_byte s in 239 | let n = S.take_big len s in 240 | `Bignum (if sign > 0 then minus_big_int n else n) 241 | (* LARGE_BIG_EXT *) 242 | | '\111' -> 243 | let len = S.take_int32 s in 244 | let sign = S.take_byte s in 245 | let n = S.take_big len s in 246 | `Bignum (if sign > 0 then minus_big_int n else n) 247 | 248 | (* NEW_REFERENCE_EXT *) 249 | | '\114' -> 250 | let len = S.take_word s in 251 | let node = parse s in 252 | let creation = S.take_byte s in 253 | let id = S.take_string (len * 4) s in 254 | let node = match node with 255 | | `Atom s -> s 256 | | #t -> failwithf "Unexpected NEW_REFERENCE_EXT format" 257 | in `NewReference (node, creation, id) 258 | 259 | (* FUN_EXT *) 260 | (* NEW_FUN_EXT *) 261 | (* EXPORT_EXT *) 262 | 263 | (* BIT_BINARY_EXT *) 264 | | '\077' -> 265 | let len = S.take_int32 s in 266 | let bits = S.take_byte s in 267 | let blob = S.take_string len s in 268 | let buf = Buffer.create len in begin 269 | Buffer.add_string buf blob; 270 | `BitBinary (buf, bits) 271 | end 272 | 273 | (* Unknown tag? *) 274 | | tag -> invalid_arg (String.make 1 tag) 275 | 276 | 277 | (* +------------+ 278 | | Serializer | 279 | +------------+ *) 280 | 281 | let rec serialize buf term = 282 | let add f x = 283 | let res = f x in 284 | (* We have to use explicit loop, because of the Buffer API change 285 | in OCaml>=4.02. *) 286 | for i = 0 to Bytes.length res - 1 do 287 | Buffer.add_char buf (Bytes.get res i) 288 | done 289 | in 290 | 291 | let open Buffer in match term with 292 | | `Int n when n >= 0 && n < 256 -> 293 | begin add pack_byte 97; add pack_byte n end 294 | | `Int n -> 295 | begin add pack_byte 98; add pack_int32 n end 296 | | `Float f -> 297 | let s = Printf.sprintf "%.20e" f in begin 298 | add pack_byte 99; 299 | add_string buf s; 300 | add_string buf <| String.make (31 - String.length s) '\000' 301 | end 302 | | `Atom s -> 303 | let length = String.length s in 304 | if length < 256 then ( 305 | add pack_byte 100; 306 | add pack_word length; 307 | add_string buf s 308 | ) else 309 | failwithf "ATOM_EXT length exceeded %i > 255" length 310 | | `Reference (node, id, creation) -> 311 | begin 312 | add pack_byte 101; 313 | serialize buf (`Atom node); (* reuse the buf we already have. *) 314 | Buffer.add_string buf (String.resize id 4); 315 | add pack_byte creation 316 | end 317 | | `Port (node, id, creation) -> 318 | begin 319 | add pack_byte 102; 320 | serialize buf (`Atom node); 321 | add pack_int32 id; 322 | add pack_byte creation 323 | end 324 | | `PID (node, id, serial, creation) -> 325 | add pack_byte 103; 326 | serialize buf (`Atom node); 327 | Buffer.add_string buf (String.resize id 4); 328 | add pack_int32 serial; 329 | add pack_byte creation 330 | | `Tuple l -> 331 | let length = List.length l in 332 | if length < 256 then ( 333 | add pack_byte 104; 334 | add pack_byte length; 335 | ) else ( 336 | add pack_byte 105; 337 | add pack_int32 length; 338 | ); 339 | List.iter (serialize buf) l 340 | | `List [] -> add pack_byte 106 341 | | `List l -> 342 | begin 343 | add pack_byte 108; 344 | add pack_int32 (List.length l); 345 | List.iter (serialize buf) l; 346 | add pack_byte 106 347 | end 348 | | `String s -> 349 | let length = String.length s in 350 | if length < 65536 then ( 351 | add pack_byte 107; 352 | add pack_word length; 353 | add_string buf s 354 | ) else ( 355 | (* Strings longer than 65535 bytes are encoded as LIST_EXT. 356 | No explicit convertion to [List] is done, because it really 357 | slows things down for large strings. *) 358 | add pack_byte 108; 359 | add pack_int32 (String.length s); 360 | String.iter (fun c -> add_char buf '\097'; add_char buf c) s; 361 | add pack_byte 106 362 | ) 363 | | `Binary b -> 364 | begin 365 | add pack_byte 109; 366 | add pack_int32 (Buffer.length b); 367 | add_buffer buf b 368 | end 369 | | `Bignum n -> 370 | let sign = match sign_big_int n with -1 -> 1 | _ -> 0 in 371 | let digits = big_int_digits (abs_big_int n) in 372 | let length = List.length digits in 373 | if length < 256 then ( 374 | add pack_byte 110; 375 | add pack_byte length; 376 | ) else ( 377 | add pack_byte 111; 378 | add pack_int32 length; 379 | ); 380 | add pack_byte sign; 381 | List.iter (fun d -> add pack_byte d) digits 382 | | `NewReference (node, creation, id) -> 383 | let len = (String.length id) / 4 in 384 | add pack_byte 114; 385 | add pack_word len; 386 | serialize buf (`Atom node); 387 | add pack_byte creation; 388 | Buffer.add_string buf (String.resize id (len * 4)) 389 | | `BitBinary (b, 0) -> serialize buf (`Binary b) 390 | | `BitBinary (b, bits) -> 391 | begin 392 | add pack_byte 77; 393 | add pack_int32 (Buffer.length b); 394 | add pack_byte bits; 395 | add_buffer buf b 396 | end 397 | 398 | 399 | (* +------------+ 400 | | Public API | 401 | +------------+ *) 402 | 403 | let decode_exn s = 404 | match s.[0] with 405 | | '\131' -> 406 | let stream = S.of_string s in 407 | let (_ : char) = S.next stream in 408 | parse stream 409 | | n -> 410 | failwithf "Erlang binary doesn't start with 131: %i" (int_of_char n) 411 | 412 | and encode_exn term = 413 | let buf = Buffer.create 256 in begin 414 | Buffer.add_char buf '\131'; 415 | serialize buf term; 416 | Buffer.contents buf 417 | end 418 | 419 | 420 | let decode s = 421 | try 422 | Some (decode_exn s) 423 | with _ -> None 424 | 425 | and encode term = 426 | try 427 | Some (encode_exn term) 428 | with _ -> None 429 | 430 | 431 | (* +------------------+ 432 | * | Pretty printers. | 433 | * +------------------+ *) 434 | 435 | let rec to_string = function 436 | | `Int x -> string_of_int x 437 | | `Float f -> string_of_float f 438 | | `Atom s | `String s -> s 439 | | `Reference (node, id, creation) | `NewReference (node, creation, id) -> 440 | Printf.sprintf "#Ref<[%s]:%s.%i>" node id creation 441 | | `Port (node, id, creation) -> 442 | Printf.sprintf "#Port<[%s]:%i.%i>" node id creation 443 | | `PID (node, id, serial, creation) -> 444 | Printf.sprintf "#PID<[%s]:%s.%i.%i>" node id serial creation 445 | | `Tuple terms -> 446 | Printf.sprintf "{%s}" (String.concat "," (List.map to_string terms)) 447 | | `List terms -> 448 | Printf.sprintf "[%s]" (String.concat "," (List.map to_string terms)) 449 | | `Binary b -> Printf.sprintf "<<%s>>" (Buffer.contents b) 450 | | `Bignum n -> string_of_big_int n 451 | | `BitBinary (b, bits) -> Printf.sprintf "<<%s:%i>>" (Buffer.contents b) bits 452 | 453 | 454 | (* +-----------------------------------------+ 455 | | Here come Erlang ports! -- the easy way | 456 | +-----------------------------------------+ *) 457 | 458 | let rec read_term ic = 459 | let len = 460 | try 461 | input_binary_int ic 462 | with End_of_file -> exit 0 in 463 | let buf = Bytes.create len in begin 464 | really_input ic buf 0 len; 465 | decode_exn (Bytes.to_string buf) 466 | end 467 | 468 | and write_term oc term = 469 | let buf = encode_exn term in begin 470 | output_binary_int oc (String.length buf); 471 | output_string oc buf; 472 | flush oc 473 | end 474 | 475 | and interact f = 476 | let rec transform state = function 477 | | `Atom "stop" -> exit 0 478 | | `Atom "ping" -> state, `Atom "pong" 479 | | term -> 480 | let term = try f term with exn -> 481 | `Tuple [`Atom "error"; 482 | `Tuple [term; `String (Printexc.to_string exn)]] 483 | in (state, term) 484 | and loop state = 485 | let (state, term) = transform state <| read_term stdin in begin 486 | write_term stdout term; 487 | loop state 488 | end 489 | in begin 490 | set_binary_mode_in stdin true; 491 | set_binary_mode_out stdout true; 492 | 493 | try loop 0 with End_of_file -> exit 0; 494 | 495 | end 496 | -------------------------------------------------------------------------------- /src/alberto.mldylib: -------------------------------------------------------------------------------- 1 | # OASIS_START 2 | # DO NOT EDIT (digest: 098a50f333b9fc79b806dd51ed4ea859) 3 | Alberto 4 | # OASIS_STOP 5 | -------------------------------------------------------------------------------- /src/alberto.mli: -------------------------------------------------------------------------------- 1 | (** Alberto knows how to handle Erlang. 2 | 3 | Alberto is an implementation of 4 | {{: http://erlang.org/doc/apps/erts/erl_ext_dist.html} Erlang External Term Format}, 5 | which is a protocol, used by Erlang nodes to communicate with so 6 | called {e ports}. See Erlang 7 | {{: http://www.erlang.org/doc/tutorial/c_port.html} documentation} 8 | for details. 9 | 10 | @author Sergei Lebedev 11 | @version 0.4 12 | *) 13 | 14 | open Big_int 15 | 16 | type t = [ `Int of int 17 | | `Float of float 18 | | `Atom of string 19 | | `Reference of (string * string * int) 20 | | `Port of (string * int * int) 21 | | `PID of (string * string * int * int) 22 | | `Tuple of t list 23 | | `List of t list 24 | | `String of string 25 | | `Binary of Buffer.t 26 | | `Bignum of big_int 27 | | `NewReference of (string * int * string) 28 | | `BitBinary of (Buffer.t * int) 29 | ] 30 | 31 | 32 | val encode : t -> string option 33 | (** [encode t] serializes a given Alberto term to a binary string. *) 34 | 35 | val decode : string -> t option 36 | (** [decode t] deserializes Alberto term from a binary string. *) 37 | 38 | 39 | val decode_exn : string -> t 40 | (** [encode_exn t] deserializes Alberto term from a binary string. Raises 41 | [Failure] if a given string doesn't contain a valid Alberto term. *) 42 | 43 | val encode_exn : t -> string 44 | (** [encode_exn t] serializes a given Alberto term to a binary string. 45 | Raises [Failure] if a given term can't be encoded. *) 46 | 47 | val to_string : t -> string 48 | (** [to_string t] converts a given Alberto term to its string representation. *) 49 | 50 | 51 | val read_term : in_channel -> t 52 | (** [read_term ic] fetches an Erlang [!term] from the port channel [ic]. *) 53 | 54 | val write_term : out_channel -> t -> unit 55 | (** [write_term oc term] serializes a given Erlang [!term] to the port 56 | channel [oc]. *) 57 | 58 | val interact : (t -> t) -> unit 59 | (** [interact f] main port loop, recieves Erlang terms from stdin, 60 | executes a given key function [f] and sends back the results. *) 61 | -------------------------------------------------------------------------------- /src/alberto.mllib: -------------------------------------------------------------------------------- 1 | # OASIS_START 2 | # DO NOT EDIT (digest: 098a50f333b9fc79b806dd51ed4ea859) 3 | Alberto 4 | # OASIS_STOP 5 | -------------------------------------------------------------------------------- /src/dune: -------------------------------------------------------------------------------- 1 | (library 2 | (name alberto) 3 | (public_name alberto) 4 | (synopsis "OCaml interface to Erlang ports") 5 | (libraries ocplib-endian num bytes) 6 | (flags :standard -safe-string)) 7 | -------------------------------------------------------------------------------- /tests.ml: -------------------------------------------------------------------------------- 1 | (** Tests for Alberto module. *) 2 | 3 | open Kaputt.Abbreviations 4 | 5 | 6 | module String = struct 7 | include String 8 | 9 | let resize s n = 10 | if length s > n 11 | then sub s 0 n 12 | else 13 | let buf = Bytes.make n '\000' in 14 | blit s 0 buf 0 (length s); 15 | Bytes.to_string buf 16 | end 17 | 18 | 19 | module ErlGen = struct 20 | module G = Gen 21 | 22 | (* Helpers. *) 23 | let pos_int8 = G.make_int 0 (1 lsl 8 - 1) 24 | let pos_int16 = G.make_int 0 (1 lsl 16 - 1) 25 | 26 | (* Primitive types. *) 27 | let small_int = G.map1 (fun x -> `Int x) Alberto.to_string pos_int8 28 | let integer = G.map1 (fun x -> `Int x) Alberto.to_string 29 | (G.map1 Int32.to_int string_of_int G.int32) 30 | let atom = G.map1 (fun s -> `Atom s) Alberto.to_string (G.word pos_int8) 31 | let float = G.map1 (fun f -> `Float f) Alberto.to_string G.float 32 | let reference = G.map1 (fun r -> `Reference r) Alberto.to_string 33 | G.(zip3 34 | (word pos_int8) 35 | (word (make_int 0 16)) 36 | pos_int8) 37 | let port = G.map1 (fun p -> `Port p) Alberto.to_string 38 | G.(zip3 39 | (word pos_int8) 40 | (map1 Int32.to_int string_of_int pos_int32) 41 | pos_int8) 42 | let pid = G.map1 (fun p -> `PID p) Alberto.to_string 43 | G.(zip4 44 | (word pos_int8) 45 | (word (make_int 1 4)) 46 | (map1 Int32.to_int string_of_int int32) 47 | pos_int8) 48 | let nil = G.map1 (fun _ -> `List []) Alberto.to_string G.unit 49 | let small_string = G.map1 (fun s -> `String s) Alberto.to_string 50 | (G.word pos_int16) 51 | let large_string = G.map1 (fun s -> `String s) Alberto.to_string 52 | G.(word (make_int (1 lsl 16) (1 lsl 20))) 53 | let binary = G.map1 (fun b -> `Binary b) Alberto.to_string 54 | (G.buffer (G.word pos_int16)) 55 | let small_big = G.map1 (fun n -> `Bignum n) Alberto.to_string 56 | (KaputtNums.Generator.big_int pos_int8) 57 | let large_big = G.map1 (fun n -> `Bignum n) Alberto.to_string 58 | (KaputtNums.Generator.big_int pos_int16) 59 | let new_reference = G.map1 60 | (fun (n, c) -> `NewReference 61 | (n, c, String.(make ((length n) * 4) '\000'))) 62 | Alberto.to_string 63 | G.(zip2 (word pos_int8) pos_int8) 64 | let bit_binary = G.map1 (fun b -> `BitBinary b) Alberto.to_string 65 | G.(zip2 66 | (G.buffer (word pos_int8)) 67 | pos_int8) 68 | 69 | (* Compound types. *) 70 | let term = G.choose_list [small_int; integer; atom; float; port; nil] 71 | 72 | let small_tuple = G.map1 (fun l -> `Tuple l) Alberto.to_string 73 | (G.list pos_int8 term) 74 | let large_tuple = G.map1 (fun l -> `Tuple l) Alberto.to_string 75 | (G.list pos_int16 term) 76 | let list = G.map1 (fun l -> `List l) Alberto.to_string (G.list pos_int16 term) 77 | end 78 | 79 | 80 | module Spec = struct 81 | include Spec 82 | 83 | let id (x, y) = x = y 84 | end 85 | 86 | 87 | let parse_unparse term = Alberto.decode_exn (Alberto.encode_exn term);; 88 | 89 | 90 | Test.add_random_test 91 | ~title:"SMALL_INTEGER_EXT" 92 | ~nb_runs:500 93 | ErlGen.small_int 94 | parse_unparse 95 | [Spec.always => Spec.id] 96 | ;; 97 | 98 | 99 | Test.add_random_test 100 | ~title:"INTEGER_EXT" 101 | ~nb_runs:500 102 | ErlGen.integer 103 | parse_unparse 104 | [Spec.always => Spec.id] 105 | ;; 106 | 107 | 108 | Test.add_random_test 109 | ~title:"FLOAT_EXT" 110 | ~nb_runs:500 111 | ErlGen.float 112 | parse_unparse 113 | [Spec.always => Spec.id] 114 | ;; 115 | 116 | 117 | Test.add_random_test 118 | ~title:"ATOM_EXT" 119 | ~nb_runs:500 120 | ErlGen.atom 121 | parse_unparse 122 | [Spec.always => Spec.id] 123 | ;; 124 | 125 | 126 | Test.add_random_test 127 | ~title:"REFERENCE_EXT" 128 | ErlGen.reference 129 | parse_unparse 130 | [Spec.always => function 131 | | (`Reference (node, id, creation), r) -> 132 | r = (`Reference (node, (String.resize id 4), creation)) 133 | | (_, _) -> false] 134 | ;; 135 | 136 | 137 | Test.add_random_test 138 | ~title:"PORT_EXT" 139 | ErlGen.port 140 | parse_unparse 141 | [Spec.always => Spec.id] 142 | ;; 143 | 144 | 145 | Test.add_random_test 146 | ~title:"PID_EXT" 147 | ErlGen.pid 148 | parse_unparse 149 | [Spec.always => function 150 | | (`PID (node, id, serial, creation), p) -> 151 | p = (`PID (node, (String.resize id 4), serial, creation)) 152 | | (_, _) -> false] 153 | ;; 154 | 155 | 156 | Test.add_random_test 157 | ~title:"SMALL_TUPLE_EXT" 158 | ErlGen.small_tuple 159 | parse_unparse 160 | [Spec.always => Spec.id] 161 | ;; 162 | 163 | 164 | Test.add_random_test 165 | ~title:"LARGE_TUPLE_EXT" 166 | ~nb_runs:10 167 | ErlGen.large_tuple 168 | parse_unparse 169 | [Spec.always => Spec.id] 170 | ;; 171 | 172 | 173 | Test.add_random_test 174 | ~title:"NIL_EXT" 175 | ErlGen.nil 176 | parse_unparse 177 | [Spec.always => Spec.id] 178 | ;; 179 | 180 | 181 | Test.add_random_test 182 | ~title:"STRING_EXT (small)" 183 | ~nb_runs:500 184 | ErlGen.small_string 185 | parse_unparse 186 | [Spec.always => Spec.id] 187 | ;; 188 | 189 | 190 | Test.add_random_test 191 | ~title:"STRING_EXT (large)" 192 | ~nb_runs:10 193 | ErlGen.large_string 194 | parse_unparse 195 | [Spec.always => function 196 | | (`String _, `List _) -> true 197 | | _ -> false] 198 | ;; 199 | 200 | 201 | Test.add_random_test 202 | ~title:"LIST_EXT" 203 | ~nb_runs:10 204 | ErlGen.list 205 | parse_unparse 206 | [Spec.always => Spec.id] 207 | ;; 208 | 209 | 210 | Test.add_random_test 211 | ~title:"BINARY_EXT" 212 | ErlGen.binary 213 | parse_unparse 214 | [Spec.always => function 215 | | (`Binary b, `Binary b') -> (Buffer.contents b) = (Buffer.contents b') 216 | | _ -> false] 217 | ;; 218 | 219 | 220 | Test.add_random_test 221 | ~title:"SMALL_BIG_EXT" 222 | ErlGen.small_big 223 | parse_unparse 224 | [Spec.always => function 225 | | (`Bignum n, `Bignum n') -> Big_int.eq_big_int n n' 226 | | _ -> false] 227 | ;; 228 | 229 | 230 | Test.add_random_test 231 | ~title:"LARGE_BIG_EXT" 232 | ~nb_runs:10 233 | ErlGen.large_big 234 | parse_unparse 235 | [Spec.always => function 236 | | (`Bignum n, `Bignum n') -> Big_int.eq_big_int n n' 237 | | _ -> false] 238 | ;; 239 | 240 | 241 | Test.add_random_test 242 | ~title:"NEW_REFERENCE_EXT" 243 | ErlGen.new_reference 244 | parse_unparse 245 | [Spec.always => function 246 | | (`NewReference (node, creation, id), r) -> 247 | let len = (String.length id) / 4 in 248 | r = (`NewReference (node, creation, (String.resize id (len * 4)))) 249 | | (_, _) -> false] 250 | ;; 251 | 252 | 253 | Test.add_random_test 254 | ~title:"BIT_BINARY_EXT" 255 | ErlGen.bit_binary 256 | parse_unparse 257 | [Spec.always => function 258 | | (`BitBinary (b, 0), `Binary b') -> 259 | (Buffer.contents b) = (Buffer.contents b') 260 | | (`BitBinary (b, bits), `BitBinary (b', bits')) -> 261 | (Buffer.contents b) = (Buffer.contents b') && bits = bits' 262 | | _ -> false] 263 | ;; 264 | 265 | 266 | let () = 267 | Test.launch_tests () 268 | ;; 269 | --------------------------------------------------------------------------------