├── .gitignore ├── LICENSE ├── Makefile ├── README.md ├── beardbolt-benchmark.el ├── beardbolt.el ├── beardbolt.gif └── starters ├── beardbolt.c ├── beardbolt.cpp ├── beardbolt.rs ├── slow-to-process.cpp ├── unordered-multimap-emplace.cpp └── vector-emplace-back.cpp /.gitignore: -------------------------------------------------------------------------------- 1 | 2 | *.elc 3 | *-autoloads.el 4 | *-pkg.el 5 | /.cask/ 6 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | GNU AFFERO GENERAL PUBLIC LICENSE 2 | Version 3, 19 November 2007 3 | 4 | Copyright (C) 2007 Free Software Foundation, Inc. 5 | Everyone is permitted to copy and distribute verbatim copies 6 | of this license document, but changing it is not allowed. 7 | 8 | Preamble 9 | 10 | The GNU Affero General Public License is a free, copyleft license for 11 | software and other kinds of works, specifically designed to ensure 12 | cooperation with the community in the case of network server software. 13 | 14 | The licenses for most software and other practical works are designed 15 | to take away your freedom to share and change the works. By contrast, 16 | our General Public Licenses are intended to guarantee your freedom to 17 | share and change all versions of a program--to make sure it remains free 18 | software for all its users. 19 | 20 | When we speak of free software, we are referring to freedom, not 21 | price. Our General Public Licenses are designed to make sure that you 22 | have the freedom to distribute copies of free software (and charge for 23 | them if you wish), that you receive source code or can get it if you 24 | want it, that you can change the software or use pieces of it in new 25 | free programs, and that you know you can do these things. 26 | 27 | Developers that use our General Public Licenses protect your rights 28 | with two steps: (1) assert copyright on the software, and (2) offer 29 | you this License which gives you legal permission to copy, distribute 30 | and/or modify the software. 31 | 32 | A secondary benefit of defending all users' freedom is that 33 | improvements made in alternate versions of the program, if they 34 | receive widespread use, become available for other developers to 35 | incorporate. Many developers of free software are heartened and 36 | encouraged by the resulting cooperation. However, in the case of 37 | software used on network servers, this result may fail to come about. 38 | The GNU General Public License permits making a modified version and 39 | letting the public access it on a server without ever releasing its 40 | source code to the public. 41 | 42 | The GNU Affero General Public License is designed specifically to 43 | ensure that, in such cases, the modified source code becomes available 44 | to the community. It requires the operator of a network server to 45 | provide the source code of the modified version running there to the 46 | users of that server. Therefore, public use of a modified version, on 47 | a publicly accessible server, gives the public access to the source 48 | code of the modified version. 49 | 50 | An older license, called the Affero General Public License and 51 | published by Affero, was designed to accomplish similar goals. This is 52 | a different license, not a version of the Affero GPL, but Affero has 53 | released a new version of the Affero GPL which permits relicensing under 54 | this license. 55 | 56 | The precise terms and conditions for copying, distribution and 57 | modification follow. 58 | 59 | TERMS AND CONDITIONS 60 | 61 | 0. Definitions. 62 | 63 | "This License" refers to version 3 of the GNU Affero General Public License. 64 | 65 | "Copyright" also means copyright-like laws that apply to other kinds of 66 | works, such as semiconductor masks. 67 | 68 | "The Program" refers to any copyrightable work licensed under this 69 | License. Each licensee is addressed as "you". "Licensees" and 70 | "recipients" may be individuals or organizations. 71 | 72 | To "modify" a work means to copy from or adapt all or part of the work 73 | in a fashion requiring copyright permission, other than the making of an 74 | exact copy. The resulting work is called a "modified version" of the 75 | earlier work or a work "based on" the earlier work. 76 | 77 | A "covered work" means either the unmodified Program or a work based 78 | on the Program. 79 | 80 | To "propagate" a work means to do anything with it that, without 81 | permission, would make you directly or secondarily liable for 82 | infringement under applicable copyright law, except executing it on a 83 | computer or modifying a private copy. Propagation includes copying, 84 | distribution (with or without modification), making available to the 85 | public, and in some countries other activities as well. 86 | 87 | To "convey" a work means any kind of propagation that enables other 88 | parties to make or receive copies. Mere interaction with a user through 89 | a computer network, with no transfer of a copy, is not conveying. 90 | 91 | An interactive user interface displays "Appropriate Legal Notices" 92 | to the extent that it includes a convenient and prominently visible 93 | feature that (1) displays an appropriate copyright notice, and (2) 94 | tells the user that there is no warranty for the work (except to the 95 | extent that warranties are provided), that licensees may convey the 96 | work under this License, and how to view a copy of this License. If 97 | the interface presents a list of user commands or options, such as a 98 | menu, a prominent item in the list meets this criterion. 99 | 100 | 1. Source Code. 101 | 102 | The "source code" for a work means the preferred form of the work 103 | for making modifications to it. "Object code" means any non-source 104 | form of a work. 105 | 106 | A "Standard Interface" means an interface that either is an official 107 | standard defined by a recognized standards body, or, in the case of 108 | interfaces specified for a particular programming language, one that 109 | is widely used among developers working in that language. 110 | 111 | The "System Libraries" of an executable work include anything, other 112 | than the work as a whole, that (a) is included in the normal form of 113 | packaging a Major Component, but which is not part of that Major 114 | Component, and (b) serves only to enable use of the work with that 115 | Major Component, or to implement a Standard Interface for which an 116 | implementation is available to the public in source code form. A 117 | "Major Component", in this context, means a major essential component 118 | (kernel, window system, and so on) of the specific operating system 119 | (if any) on which the executable work runs, or a compiler used to 120 | produce the work, or an object code interpreter used to run it. 121 | 122 | The "Corresponding Source" for a work in object code form means all 123 | the source code needed to generate, install, and (for an executable 124 | work) run the object code and to modify the work, including scripts to 125 | control those activities. However, it does not include the work's 126 | System Libraries, or general-purpose tools or generally available free 127 | programs which are used unmodified in performing those activities but 128 | which are not part of the work. For example, Corresponding Source 129 | includes interface definition files associated with source files for 130 | the work, and the source code for shared libraries and dynamically 131 | linked subprograms that the work is specifically designed to require, 132 | such as by intimate data communication or control flow between those 133 | subprograms and other parts of the work. 134 | 135 | The Corresponding Source need not include anything that users 136 | can regenerate automatically from other parts of the Corresponding 137 | Source. 138 | 139 | The Corresponding Source for a work in source code form is that 140 | same work. 141 | 142 | 2. Basic Permissions. 143 | 144 | All rights granted under this License are granted for the term of 145 | copyright on the Program, and are irrevocable provided the stated 146 | conditions are met. This License explicitly affirms your unlimited 147 | permission to run the unmodified Program. The output from running a 148 | covered work is covered by this License only if the output, given its 149 | content, constitutes a covered work. This License acknowledges your 150 | rights of fair use or other equivalent, as provided by copyright law. 151 | 152 | You may make, run and propagate covered works that you do not 153 | convey, without conditions so long as your license otherwise remains 154 | in force. You may convey covered works to others for the sole purpose 155 | of having them make modifications exclusively for you, or provide you 156 | with facilities for running those works, provided that you comply with 157 | the terms of this License in conveying all material for which you do 158 | not control copyright. Those thus making or running the covered works 159 | for you must do so exclusively on your behalf, under your direction 160 | and control, on terms that prohibit them from making any copies of 161 | your copyrighted material outside their relationship with you. 162 | 163 | Conveying under any other circumstances is permitted solely under 164 | the conditions stated below. Sublicensing is not allowed; section 10 165 | makes it unnecessary. 166 | 167 | 3. Protecting Users' Legal Rights From Anti-Circumvention Law. 168 | 169 | No covered work shall be deemed part of an effective technological 170 | measure under any applicable law fulfilling obligations under article 171 | 11 of the WIPO copyright treaty adopted on 20 December 1996, or 172 | similar laws prohibiting or restricting circumvention of such 173 | measures. 174 | 175 | When you convey a covered work, you waive any legal power to forbid 176 | circumvention of technological measures to the extent such circumvention 177 | is effected by exercising rights under this License with respect to 178 | the covered work, and you disclaim any intention to limit operation or 179 | modification of the work as a means of enforcing, against the work's 180 | users, your or third parties' legal rights to forbid circumvention of 181 | technological measures. 182 | 183 | 4. Conveying Verbatim Copies. 184 | 185 | You may convey verbatim copies of the Program's source code as you 186 | receive it, in any medium, provided that you conspicuously and 187 | appropriately publish on each copy an appropriate copyright notice; 188 | keep intact all notices stating that this License and any 189 | non-permissive terms added in accord with section 7 apply to the code; 190 | keep intact all notices of the absence of any warranty; and give all 191 | recipients a copy of this License along with the Program. 192 | 193 | You may charge any price or no price for each copy that you convey, 194 | and you may offer support or warranty protection for a fee. 195 | 196 | 5. Conveying Modified Source Versions. 197 | 198 | You may convey a work based on the Program, or the modifications to 199 | produce it from the Program, in the form of source code under the 200 | terms of section 4, provided that you also meet all of these conditions: 201 | 202 | a) The work must carry prominent notices stating that you modified 203 | it, and giving a relevant date. 204 | 205 | b) The work must carry prominent notices stating that it is 206 | released under this License and any conditions added under section 207 | 7. This requirement modifies the requirement in section 4 to 208 | "keep intact all notices". 209 | 210 | c) You must license the entire work, as a whole, under this 211 | License to anyone who comes into possession of a copy. This 212 | License will therefore apply, along with any applicable section 7 213 | additional terms, to the whole of the work, and all its parts, 214 | regardless of how they are packaged. This License gives no 215 | permission to license the work in any other way, but it does not 216 | invalidate such permission if you have separately received it. 217 | 218 | d) If the work has interactive user interfaces, each must display 219 | Appropriate Legal Notices; however, if the Program has interactive 220 | interfaces that do not display Appropriate Legal Notices, your 221 | work need not make them do so. 222 | 223 | A compilation of a covered work with other separate and independent 224 | works, which are not by their nature extensions of the covered work, 225 | and which are not combined with it such as to form a larger program, 226 | in or on a volume of a storage or distribution medium, is called an 227 | "aggregate" if the compilation and its resulting copyright are not 228 | used to limit the access or legal rights of the compilation's users 229 | beyond what the individual works permit. Inclusion of a covered work 230 | in an aggregate does not cause this License to apply to the other 231 | parts of the aggregate. 232 | 233 | 6. Conveying Non-Source Forms. 234 | 235 | You may convey a covered work in object code form under the terms 236 | of sections 4 and 5, provided that you also convey the 237 | machine-readable Corresponding Source under the terms of this License, 238 | in one of these ways: 239 | 240 | a) Convey the object code in, or embodied in, a physical product 241 | (including a physical distribution medium), accompanied by the 242 | Corresponding Source fixed on a durable physical medium 243 | customarily used for software interchange. 244 | 245 | b) Convey the object code in, or embodied in, a physical product 246 | (including a physical distribution medium), accompanied by a 247 | written offer, valid for at least three years and valid for as 248 | long as you offer spare parts or customer support for that product 249 | model, to give anyone who possesses the object code either (1) a 250 | copy of the Corresponding Source for all the software in the 251 | product that is covered by this License, on a durable physical 252 | medium customarily used for software interchange, for a price no 253 | more than your reasonable cost of physically performing this 254 | conveying of source, or (2) access to copy the 255 | Corresponding Source from a network server at no charge. 256 | 257 | c) Convey individual copies of the object code with a copy of the 258 | written offer to provide the Corresponding Source. This 259 | alternative is allowed only occasionally and noncommercially, and 260 | only if you received the object code with such an offer, in accord 261 | with subsection 6b. 262 | 263 | d) Convey the object code by offering access from a designated 264 | place (gratis or for a charge), and offer equivalent access to the 265 | Corresponding Source in the same way through the same place at no 266 | further charge. You need not require recipients to copy the 267 | Corresponding Source along with the object code. If the place to 268 | copy the object code is a network server, the Corresponding Source 269 | may be on a different server (operated by you or a third party) 270 | that supports equivalent copying facilities, provided you maintain 271 | clear directions next to the object code saying where to find the 272 | Corresponding Source. Regardless of what server hosts the 273 | Corresponding Source, you remain obligated to ensure that it is 274 | available for as long as needed to satisfy these requirements. 275 | 276 | e) Convey the object code using peer-to-peer transmission, provided 277 | you inform other peers where the object code and Corresponding 278 | Source of the work are being offered to the general public at no 279 | charge under subsection 6d. 280 | 281 | A separable portion of the object code, whose source code is excluded 282 | from the Corresponding Source as a System Library, need not be 283 | included in conveying the object code work. 284 | 285 | A "User Product" is either (1) a "consumer product", which means any 286 | tangible personal property which is normally used for personal, family, 287 | or household purposes, or (2) anything designed or sold for incorporation 288 | into a dwelling. In determining whether a product is a consumer product, 289 | doubtful cases shall be resolved in favor of coverage. For a particular 290 | product received by a particular user, "normally used" refers to a 291 | typical or common use of that class of product, regardless of the status 292 | of the particular user or of the way in which the particular user 293 | actually uses, or expects or is expected to use, the product. A product 294 | is a consumer product regardless of whether the product has substantial 295 | commercial, industrial or non-consumer uses, unless such uses represent 296 | the only significant mode of use of the product. 297 | 298 | "Installation Information" for a User Product means any methods, 299 | procedures, authorization keys, or other information required to install 300 | and execute modified versions of a covered work in that User Product from 301 | a modified version of its Corresponding Source. The information must 302 | suffice to ensure that the continued functioning of the modified object 303 | code is in no case prevented or interfered with solely because 304 | modification has been made. 305 | 306 | If you convey an object code work under this section in, or with, or 307 | specifically for use in, a User Product, and the conveying occurs as 308 | part of a transaction in which the right of possession and use of the 309 | User Product is transferred to the recipient in perpetuity or for a 310 | fixed term (regardless of how the transaction is characterized), the 311 | Corresponding Source conveyed under this section must be accompanied 312 | by the Installation Information. But this requirement does not apply 313 | if neither you nor any third party retains the ability to install 314 | modified object code on the User Product (for example, the work has 315 | been installed in ROM). 316 | 317 | The requirement to provide Installation Information does not include a 318 | requirement to continue to provide support service, warranty, or updates 319 | for a work that has been modified or installed by the recipient, or for 320 | the User Product in which it has been modified or installed. Access to a 321 | network may be denied when the modification itself materially and 322 | adversely affects the operation of the network or violates the rules and 323 | protocols for communication across the network. 324 | 325 | Corresponding Source conveyed, and Installation Information provided, 326 | in accord with this section must be in a format that is publicly 327 | documented (and with an implementation available to the public in 328 | source code form), and must require no special password or key for 329 | unpacking, reading or copying. 330 | 331 | 7. Additional Terms. 332 | 333 | "Additional permissions" are terms that supplement the terms of this 334 | License by making exceptions from one or more of its conditions. 335 | Additional permissions that are applicable to the entire Program shall 336 | be treated as though they were included in this License, to the extent 337 | that they are valid under applicable law. If additional permissions 338 | apply only to part of the Program, that part may be used separately 339 | under those permissions, but the entire Program remains governed by 340 | this License without regard to the additional permissions. 341 | 342 | When you convey a copy of a covered work, you may at your option 343 | remove any additional permissions from that copy, or from any part of 344 | it. (Additional permissions may be written to require their own 345 | removal in certain cases when you modify the work.) You may place 346 | additional permissions on material, added by you to a covered work, 347 | for which you have or can give appropriate copyright permission. 348 | 349 | Notwithstanding any other provision of this License, for material you 350 | add to a covered work, you may (if authorized by the copyright holders of 351 | that material) supplement the terms of this License with terms: 352 | 353 | a) Disclaiming warranty or limiting liability differently from the 354 | terms of sections 15 and 16 of this License; or 355 | 356 | b) Requiring preservation of specified reasonable legal notices or 357 | author attributions in that material or in the Appropriate Legal 358 | Notices displayed by works containing it; or 359 | 360 | c) Prohibiting misrepresentation of the origin of that material, or 361 | requiring that modified versions of such material be marked in 362 | reasonable ways as different from the original version; or 363 | 364 | d) Limiting the use for publicity purposes of names of licensors or 365 | authors of the material; or 366 | 367 | e) Declining to grant rights under trademark law for use of some 368 | trade names, trademarks, or service marks; or 369 | 370 | f) Requiring indemnification of licensors and authors of that 371 | material by anyone who conveys the material (or modified versions of 372 | it) with contractual assumptions of liability to the recipient, for 373 | any liability that these contractual assumptions directly impose on 374 | those licensors and authors. 375 | 376 | All other non-permissive additional terms are considered "further 377 | restrictions" within the meaning of section 10. If the Program as you 378 | received it, or any part of it, contains a notice stating that it is 379 | governed by this License along with a term that is a further 380 | restriction, you may remove that term. If a license document contains 381 | a further restriction but permits relicensing or conveying under this 382 | License, you may add to a covered work material governed by the terms 383 | of that license document, provided that the further restriction does 384 | not survive such relicensing or conveying. 385 | 386 | If you add terms to a covered work in accord with this section, you 387 | must place, in the relevant source files, a statement of the 388 | additional terms that apply to those files, or a notice indicating 389 | where to find the applicable terms. 390 | 391 | Additional terms, permissive or non-permissive, may be stated in the 392 | form of a separately written license, or stated as exceptions; 393 | the above requirements apply either way. 394 | 395 | 8. Termination. 396 | 397 | You may not propagate or modify a covered work except as expressly 398 | provided under this License. Any attempt otherwise to propagate or 399 | modify it is void, and will automatically terminate your rights under 400 | this License (including any patent licenses granted under the third 401 | paragraph of section 11). 402 | 403 | However, if you cease all violation of this License, then your 404 | license from a particular copyright holder is reinstated (a) 405 | provisionally, unless and until the copyright holder explicitly and 406 | finally terminates your license, and (b) permanently, if the copyright 407 | holder fails to notify you of the violation by some reasonable means 408 | prior to 60 days after the cessation. 409 | 410 | Moreover, your license from a particular copyright holder is 411 | reinstated permanently if the copyright holder notifies you of the 412 | violation by some reasonable means, this is the first time you have 413 | received notice of violation of this License (for any work) from that 414 | copyright holder, and you cure the violation prior to 30 days after 415 | your receipt of the notice. 416 | 417 | Termination of your rights under this section does not terminate the 418 | licenses of parties who have received copies or rights from you under 419 | this License. If your rights have been terminated and not permanently 420 | reinstated, you do not qualify to receive new licenses for the same 421 | material under section 10. 422 | 423 | 9. Acceptance Not Required for Having Copies. 424 | 425 | You are not required to accept this License in order to receive or 426 | run a copy of the Program. Ancillary propagation of a covered work 427 | occurring solely as a consequence of using peer-to-peer transmission 428 | to receive a copy likewise does not require acceptance. However, 429 | nothing other than this License grants you permission to propagate or 430 | modify any covered work. These actions infringe copyright if you do 431 | not accept this License. Therefore, by modifying or propagating a 432 | covered work, you indicate your acceptance of this License to do so. 433 | 434 | 10. Automatic Licensing of Downstream Recipients. 435 | 436 | Each time you convey a covered work, the recipient automatically 437 | receives a license from the original licensors, to run, modify and 438 | propagate that work, subject to this License. You are not responsible 439 | for enforcing compliance by third parties with this License. 440 | 441 | An "entity transaction" is a transaction transferring control of an 442 | organization, or substantially all assets of one, or subdividing an 443 | organization, or merging organizations. If propagation of a covered 444 | work results from an entity transaction, each party to that 445 | transaction who receives a copy of the work also receives whatever 446 | licenses to the work the party's predecessor in interest had or could 447 | give under the previous paragraph, plus a right to possession of the 448 | Corresponding Source of the work from the predecessor in interest, if 449 | the predecessor has it or can get it with reasonable efforts. 450 | 451 | You may not impose any further restrictions on the exercise of the 452 | rights granted or affirmed under this License. For example, you may 453 | not impose a license fee, royalty, or other charge for exercise of 454 | rights granted under this License, and you may not initiate litigation 455 | (including a cross-claim or counterclaim in a lawsuit) alleging that 456 | any patent claim is infringed by making, using, selling, offering for 457 | sale, or importing the Program or any portion of it. 458 | 459 | 11. Patents. 460 | 461 | A "contributor" is a copyright holder who authorizes use under this 462 | License of the Program or a work on which the Program is based. The 463 | work thus licensed is called the contributor's "contributor version". 464 | 465 | A contributor's "essential patent claims" are all patent claims 466 | owned or controlled by the contributor, whether already acquired or 467 | hereafter acquired, that would be infringed by some manner, permitted 468 | by this License, of making, using, or selling its contributor version, 469 | but do not include claims that would be infringed only as a 470 | consequence of further modification of the contributor version. For 471 | purposes of this definition, "control" includes the right to grant 472 | patent sublicenses in a manner consistent with the requirements of 473 | this License. 474 | 475 | Each contributor grants you a non-exclusive, worldwide, royalty-free 476 | patent license under the contributor's essential patent claims, to 477 | make, use, sell, offer for sale, import and otherwise run, modify and 478 | propagate the contents of its contributor version. 479 | 480 | In the following three paragraphs, a "patent license" is any express 481 | agreement or commitment, however denominated, not to enforce a patent 482 | (such as an express permission to practice a patent or covenant not to 483 | sue for patent infringement). To "grant" such a patent license to a 484 | party means to make such an agreement or commitment not to enforce a 485 | patent against the party. 486 | 487 | If you convey a covered work, knowingly relying on a patent license, 488 | and the Corresponding Source of the work is not available for anyone 489 | to copy, free of charge and under the terms of this License, through a 490 | publicly available network server or other readily accessible means, 491 | then you must either (1) cause the Corresponding Source to be so 492 | available, or (2) arrange to deprive yourself of the benefit of the 493 | patent license for this particular work, or (3) arrange, in a manner 494 | consistent with the requirements of this License, to extend the patent 495 | license to downstream recipients. "Knowingly relying" means you have 496 | actual knowledge that, but for the patent license, your conveying the 497 | covered work in a country, or your recipient's use of the covered work 498 | in a country, would infringe one or more identifiable patents in that 499 | country that you have reason to believe are valid. 500 | 501 | If, pursuant to or in connection with a single transaction or 502 | arrangement, you convey, or propagate by procuring conveyance of, a 503 | covered work, and grant a patent license to some of the parties 504 | receiving the covered work authorizing them to use, propagate, modify 505 | or convey a specific copy of the covered work, then the patent license 506 | you grant is automatically extended to all recipients of the covered 507 | work and works based on it. 508 | 509 | A patent license is "discriminatory" if it does not include within 510 | the scope of its coverage, prohibits the exercise of, or is 511 | conditioned on the non-exercise of one or more of the rights that are 512 | specifically granted under this License. You may not convey a covered 513 | work if you are a party to an arrangement with a third party that is 514 | in the business of distributing software, under which you make payment 515 | to the third party based on the extent of your activity of conveying 516 | the work, and under which the third party grants, to any of the 517 | parties who would receive the covered work from you, a discriminatory 518 | patent license (a) in connection with copies of the covered work 519 | conveyed by you (or copies made from those copies), or (b) primarily 520 | for and in connection with specific products or compilations that 521 | contain the covered work, unless you entered into that arrangement, 522 | or that patent license was granted, prior to 28 March 2007. 523 | 524 | Nothing in this License shall be construed as excluding or limiting 525 | any implied license or other defenses to infringement that may 526 | otherwise be available to you under applicable patent law. 527 | 528 | 12. No Surrender of Others' Freedom. 529 | 530 | If conditions are imposed on you (whether by court order, agreement or 531 | otherwise) that contradict the conditions of this License, they do not 532 | excuse you from the conditions of this License. If you cannot convey a 533 | covered work so as to satisfy simultaneously your obligations under this 534 | License and any other pertinent obligations, then as a consequence you may 535 | not convey it at all. For example, if you agree to terms that obligate you 536 | to collect a royalty for further conveying from those to whom you convey 537 | the Program, the only way you could satisfy both those terms and this 538 | License would be to refrain entirely from conveying the Program. 539 | 540 | 13. Remote Network Interaction; Use with the GNU General Public License. 541 | 542 | Notwithstanding any other provision of this License, if you modify the 543 | Program, your modified version must prominently offer all users 544 | interacting with it remotely through a computer network (if your version 545 | supports such interaction) an opportunity to receive the Corresponding 546 | Source of your version by providing access to the Corresponding Source 547 | from a network server at no charge, through some standard or customary 548 | means of facilitating copying of software. This Corresponding Source 549 | shall include the Corresponding Source for any work covered by version 3 550 | of the GNU General Public License that is incorporated pursuant to the 551 | following paragraph. 552 | 553 | Notwithstanding any other provision of this License, you have 554 | permission to link or combine any covered work with a work licensed 555 | under version 3 of the GNU General Public License into a single 556 | combined work, and to convey the resulting work. The terms of this 557 | License will continue to apply to the part which is the covered work, 558 | but the work with which it is combined will remain governed by version 559 | 3 of the GNU General Public License. 560 | 561 | 14. Revised Versions of this License. 562 | 563 | The Free Software Foundation may publish revised and/or new versions of 564 | the GNU Affero General Public License from time to time. Such new versions 565 | will be similar in spirit to the present version, but may differ in detail to 566 | address new problems or concerns. 567 | 568 | Each version is given a distinguishing version number. If the 569 | Program specifies that a certain numbered version of the GNU Affero General 570 | Public License "or any later version" applies to it, you have the 571 | option of following the terms and conditions either of that numbered 572 | version or of any later version published by the Free Software 573 | Foundation. If the Program does not specify a version number of the 574 | GNU Affero General Public License, you may choose any version ever published 575 | by the Free Software Foundation. 576 | 577 | If the Program specifies that a proxy can decide which future 578 | versions of the GNU Affero General Public License can be used, that proxy's 579 | public statement of acceptance of a version permanently authorizes you 580 | to choose that version for the Program. 581 | 582 | Later license versions may give you additional or different 583 | permissions. However, no additional obligations are imposed on any 584 | author or copyright holder as a result of your choosing to follow a 585 | later version. 586 | 587 | 15. Disclaimer of Warranty. 588 | 589 | THERE IS NO WARRANTY FOR THE PROGRAM, TO THE EXTENT PERMITTED BY 590 | APPLICABLE LAW. EXCEPT WHEN OTHERWISE STATED IN WRITING THE COPYRIGHT 591 | HOLDERS AND/OR OTHER PARTIES PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY 592 | OF ANY KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, 593 | THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR 594 | PURPOSE. THE ENTIRE RISK AS TO THE QUALITY AND PERFORMANCE OF THE PROGRAM 595 | IS WITH YOU. SHOULD THE PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF 596 | ALL NECESSARY SERVICING, REPAIR OR CORRECTION. 597 | 598 | 16. Limitation of Liability. 599 | 600 | IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING 601 | WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MODIFIES AND/OR CONVEYS 602 | THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES, INCLUDING ANY 603 | GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING OUT OF THE 604 | USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED TO LOSS OF 605 | DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY YOU OR THIRD 606 | PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER PROGRAMS), 607 | EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE POSSIBILITY OF 608 | SUCH DAMAGES. 609 | 610 | 17. Interpretation of Sections 15 and 16. 611 | 612 | If the disclaimer of warranty and limitation of liability provided 613 | above cannot be given local legal effect according to their terms, 614 | reviewing courts shall apply local law that most closely approximates 615 | an absolute waiver of all civil liability in connection with the 616 | Program, unless a warranty or assumption of liability accompanies a 617 | copy of the Program in return for a fee. 618 | 619 | END OF TERMS AND CONDITIONS 620 | 621 | How to Apply These Terms to Your New Programs 622 | 623 | If you develop a new program, and you want it to be of the greatest 624 | possible use to the public, the best way to achieve this is to make it 625 | free software which everyone can redistribute and change under these terms. 626 | 627 | To do so, attach the following notices to the program. It is safest 628 | to attach them to the start of each source file to most effectively 629 | state the exclusion of warranty; and each file should have at least 630 | the "copyright" line and a pointer to where the full notice is found. 631 | 632 | 633 | Copyright (C) 634 | 635 | This program is free software: you can redistribute it and/or modify 636 | it under the terms of the GNU Affero General Public License as published by 637 | the Free Software Foundation, either version 3 of the License, or 638 | (at your option) any later version. 639 | 640 | This program is distributed in the hope that it will be useful, 641 | but WITHOUT ANY WARRANTY; without even the implied warranty of 642 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 643 | GNU Affero General Public License for more details. 644 | 645 | You should have received a copy of the GNU Affero General Public License 646 | along with this program. If not, see . 647 | 648 | Also add information on how to contact you by electronic and paper mail. 649 | 650 | If your software can interact with users remotely through a computer 651 | network, you should also make sure that it provides a way for users to 652 | get its source. For example, if your program is a web application, its 653 | interface could display a "Source" link that leads users to an archive 654 | of the code. There are many ways you could offer source, and different 655 | solutions will be better for different programs; see section 13 for the 656 | specific requirements. 657 | 658 | You should also get your employer (if you work as a programmer) or school, 659 | if any, to sign a "copyright disclaimer" for the program, if necessary. 660 | For more information on this, and how to apply and follow the GNU AGPL, see 661 | . 662 | -------------------------------------------------------------------------------- /Makefile: -------------------------------------------------------------------------------- 1 | ### Makefile for beardbolt. Lifted from Makefile for Eglot. 2 | EMACS?=emacs 3 | ELFILES := beardbolt.el 4 | ELCFILES := $(ELFILES:.el=.elc) 5 | 6 | all: compile 7 | 8 | %.elc: %.el 9 | $(EMACS) -Q -L . --batch -f batch-byte-compile $< 10 | 11 | compile: $(ELCFILES) 12 | 13 | clean: 14 | find . -iname '*.elc' -exec rm {} \; 15 | 16 | benchmark: compile 17 | $(EMACS) -Q -L . --batch -l beardbolt-benchmark starters/slow-to-process.cpp 18 | $(EMACS) -Q -L . --batch -l beardbolt-benchmark starters/vector-emplace-back.cpp 19 | $(EMACS) -Q -L . --batch -l beardbolt-benchmark starters/unordered-multimap-emplace.cpp 20 | 21 | .PHONY: all compile clean check 22 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Beardbolt 2 | 3 | ![beardbolt in action](./beardbolt.gif) 4 | 5 | A fork-rewrite of [RMSbolt](https://gitlab.com/jgkamat/rmsbolt), 6 | itself a supercharged implementation of [Godbolt, the 7 | Compiler Explorer](https://github.com/mattgodbolt/compiler-explorer) 8 | but for Emacs, instead of a clunky browser. 9 | 10 | Beardbolt shows assembly output for given source code file, making it 11 | easy to see what the compiler is doing. 12 | 13 | It also highlights which source code corresponds to a given assembly, 14 | and vice versa. 15 | 16 | ### Why Beardbolt over RMSbolt 17 | 18 | - 3-5x faster on typical files, more on larger files. See [here for 19 | benchmarks](#benchmarks). 20 | - Doesn't require file to be saved. 21 | - 🌈Has pretty rainbows🌈 22 | - Has useful Godbolt features like "execute program" and 23 | "preserve/filter library functions" . 24 | - Simpler code (less than half the LOC, though less funcional in some 25 | regards if we're honest). 26 | 27 | ### Why RMSbolt over Beardbolt 28 | 29 | - Supports way more languages/compilers. Beardbolt only C, C++ and Rust. 30 | - Supports more Emacs versions. Beardbolt probably only 28+ 31 | 32 | ### Installation 33 | 34 | ```sh 35 | cd /path/to/beardbolt/clone 36 | make 37 | ``` 38 | 39 | ```lisp 40 | (add-to-list 'load-path "/path/to/beardbolt/clone") 41 | (require 'beardbolt) 42 | ``` 43 | 44 | ``` 45 | M-x beardbolt-starter 46 | ``` 47 | 48 | ### Main commands 49 | 50 | * `beardbolt-starter`: Lets you start a new experiment in one of the 51 | supported languages. Automatically start `beardbolt-mode`. 52 | 53 | * `beardbolt-mode`: Starts a minor mode that automatically re-compiles 54 | buffer code every few changes to the source code. 55 | 56 | * `beardbolt-compile`: Manually start a compilation. Bound to `C-c 57 | C-c` in `beardbolt-mode`. 58 | 59 | ### Options as local variables 60 | 61 | Beardbolt's behaviour can be tweaked with some options that more or 62 | less correspond to the ones of Compiler Explorer. You may set them 63 | globally (they're normal Emacs customization variables), but they're 64 | probably more useful as file-local cookies, like you see in the 65 | animated gif above. 66 | 67 | Beardbolt will pick them up immediately on each run. 68 | 69 | * `beardbolt-command`: Main compiler command to run. May be something like 70 | `"gcc -O3"`. Leave unset to have Beardbolt try to guess from some 71 | nearby `compilation_commands.json`. 72 | 73 | * `beardbolt-disassemble`: Compile, assemble, then disassemble using 74 | `objdump` and present that input instead of assembly code. 75 | 76 | * `beardbolt-asm-format`: Choose between `intel` and `att` formats. 77 | 78 | * `beardbolt-preserve-directives`: Keep every non-code, non-label asm 79 | directive. 80 | 81 | * `beardbolt-preserve-unused-labels`: Keep unused asm labels. 82 | 83 | * `beardbolt-preserve-library-functions`: Keep functions with no code 84 | related to current file. 85 | 86 | * `beardbolt-demangle`: Demangle any mangled symbols of resulting 87 | assembly with `c++filt`. 88 | 89 | * `beardbolt-execute`: If non-nil, run the resulting program in the 90 | compilation buffer. If a string, run with these arguments. If `t`, 91 | runs without arguments. 92 | 93 | * `beardbolt-ccj-extra-flags`: A string of extra compilation flags to 94 | append to the compilation command devined from 95 | `compile_commands.json`. 96 | 97 | * `beardbolt-shuffle-rainbow`: Use less pretty rainbow colors, but 98 | potentially more useful and contrasting ones. 99 | 100 | 101 | ### Benchmarks vs RMSbolt 102 | 103 | First, a word on what "fast" means. The performance metric to 104 | optimize is responsiveness: the goal is not only to provide this a 105 | live view of the assembly output as quickly as possible, and also to 106 | intrude as little as possible in the user's editing. 107 | 108 | Both Beardbolt and RMSbolt extensions work in a two-step fashion. 109 | Most of the speed gains of Beardbolt happen in step 2. 110 | 111 | 1. The file is saved somewhere and partially compiled by an external 112 | program 113 | 114 | This happens asynchronously. It might takes several seconds and 115 | spin up your CPU, but it does not generally harm the UX inside 116 | Emacs. 117 | 118 | 2. Some Elisp processing takes place on the assembly output 119 | 120 | This happens inside Emacs, and it's generally bad if it takes a 121 | long time, because Emacs is single-threaded and has no easily 122 | accessible asynchronous mechanisms for this type of work. 123 | 124 | #### Results 125 | 126 | To run the benchmarks, have both RMSbolt and Beardbolt clones 127 | side-by-side, then: 128 | 129 | ``` 130 | $ cd /path/to/beardbolt/clone 131 | $ EMACS=~/Source/Emacs/emacs/src/emacs make benchmark 132 | /home/capitaomorte/Source/Emacs/emacs/src/emacs -Q -L . --batch -l beardbolt-benchmark starters/slow-to-process.cpp 133 | RMSbolt timings for slow-to-process.cpp 134 | samples: (1.329s 1.316s 1.338s 1.345s 1.341s) 135 | average: 1.334s 136 | Beardbolt timings for slow-to-process.cpp 137 | samples: (0.324s 0.338s 0.334s 0.334s 0.342s) 138 | average: 0.334s 139 | /home/capitaomorte/Source/Emacs/emacs/src/emacs -Q -L . --batch -l beardbolt-benchmark starters/vector-emplace-back.cpp 140 | RMSbolt timings for vector-emplace-back.cpp 141 | samples: (0.234s 0.223s 0.223s 0.240s 0.224s) 142 | average: 0.229s 143 | Beardbolt timings for vector-emplace-back.cpp 144 | samples: (0.086s 0.074s 0.073s 0.074s 0.089s) 145 | average: 0.079s 146 | /home/capitaomorte/Source/Emacs/emacs/src/emacs -Q -L . --batch -l beardbolt-benchmark starters/unordered-multimap-emplace.cpp 147 | RMSbolt timings for unordered-multimap-emplace.cpp 148 | samples: (0.534s 0.523s 0.524s 0.523s 0.529s) 149 | average: 0.527s 150 | Beardbolt timings for unordered-multimap-emplace.cpp 151 | samples: (0.103s 0.123s 0.103s 0.102s 0.118s) 152 | average: 0.110s 153 | ``` 154 | 155 | This ran `beardbolt-compile` and `rmsbolt-compile` 5 times on small 156 | two [cppreference.com](https://cppreference.com) examples 157 | ([1][example1], [2][example2]) as well as a known "slow" file found in 158 | [RMSbolt's bug tracker](https://gitlab.com/jgkamat/rmsbolt/-/issues/9). 159 | 160 | To make the benchmark fair(er?) I patched `rmsbolt.el` to generate 161 | slightly less debug with `-g1` instead of `-g`, and thus benefit from 162 | the same speedup that `beardbolt.el`. 163 | 164 | The results were obtained on my Thinkpad T480 running Emacs 29 165 | (without native compilation). 166 | 167 | [example1]: https://en.cppreference.com/w/cpp/container/vector/emplace_back 168 | [example2]: https://en.cppreference.com/w/cpp/container/unordered_multimap/emplace 169 | -------------------------------------------------------------------------------- /beardbolt-benchmark.el: -------------------------------------------------------------------------------- 1 | (require 'beardbolt) 2 | (require 'cl-lib) 3 | (require 'benchmark) 4 | 5 | (defvar beardbolt-benchmark-samples nil) 6 | (defvar beardbolt-benchmark-rmsbolt-samples nil) 7 | 8 | (advice-add (quote beardbolt--handle-finish-compile) :around 9 | (lambda (oldfun &rest args) 10 | (push (benchmark-elapse (apply oldfun args)) 11 | beardbolt-benchmark-samples))) 12 | 13 | (defun beardbolt-benchmark-beardbolt (repeats) 14 | (cl-loop 15 | repeat repeats 16 | do (call-interactively #'beardbolt-compile) 17 | (while (process-live-p 18 | (get-buffer-process (beardbolt--compilation-buffer))) 19 | (accept-process-output))) 20 | (message "Beardbolt timings for %s\n samples: %s\n average: %.3fs" 21 | (file-name-nondirectory buffer-file-name) 22 | (mapcar (lambda (s) (format "%.3fs" s)) 23 | (reverse beardbolt-benchmark-samples)) 24 | (/ (cl-reduce #'+ beardbolt-benchmark-samples) 25 | (length beardbolt-benchmark-samples) 1.0))) 26 | 27 | (defun beardbolt-benchmark-rmsbolt (repeats) 28 | (add-to-list 'load-path (expand-file-name "../../rmsbolt" default-directory)) 29 | (cond ((require 'rmsbolt nil t) 30 | (advice-add (quote rmsbolt--handle-finish-compile) :around 31 | (lambda (oldfun &rest args) 32 | (push (benchmark-elapse (apply oldfun args)) 33 | beardbolt-benchmark-rmsbolt-samples))) 34 | (cl-loop 35 | repeat repeats 36 | do 37 | (sit-for 0.2) ;; unconfuse RMSbolt state cleanup 38 | (rmsbolt-compile) 39 | (while (process-live-p 40 | (get-buffer-process (get-buffer "*rmsbolt-compilation*"))) 41 | (accept-process-output))) 42 | (message "RMSbolt timings for %s\n samples: %s\n average: %.3fs" 43 | (file-name-nondirectory buffer-file-name) 44 | (mapcar (lambda (s) (format "%.3fs" s)) (reverse 45 | beardbolt-benchmark-rmsbolt-samples)) 46 | (/ (cl-reduce #'+ beardbolt-benchmark-rmsbolt-samples) 47 | (length beardbolt-benchmark-rmsbolt-samples) 1.0))) 48 | (t 49 | (message "Can't find rmsbolt in load path %s" load-path)))) 50 | 51 | (with-current-buffer (find-file (car argv)) 52 | (beardbolt-benchmark-rmsbolt 5) 53 | (beardbolt-benchmark-beardbolt 5)) 54 | 55 | 56 | 57 | 58 | -------------------------------------------------------------------------------- /beardbolt.el: -------------------------------------------------------------------------------- 1 | ;;; beardbolt.el --- A compiler output viewer -*- lexical-binding: t; -*- 2 | 3 | ;; Copyright (C) 2018-2021 Jay Kamat 2022 João Távora 4 | ;; Author: João Távora 5 | ;; Version: 0.1.2 6 | ;; Keywords: compilation, tools 7 | ;; URL: https://github.com/joaotavora/beardbolt 8 | ;; Package-Requires: ((emacs "28.1")) 9 | 10 | ;; This program is free software; you can redistribute it and/or modify 11 | ;; it under the terms of the GNU Affero General Public License as published by 12 | ;; the Free Software Foundation, either version 3 of the License, or 13 | ;; (at your option) any later version. 14 | 15 | ;; This program is distributed in the hope that it will be useful, 16 | ;; but WITHOUT ANY WARRANTY; without even the implied warranty of 17 | ;; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 18 | ;; GNU Affero General Public License for more details. 19 | 20 | ;; You should have received a copy of the GNU Affero General Public License 21 | ;; along with this program. If not, see . 22 | 23 | ;;; Commentary: 24 | 25 | ;; beardbolt is a fork of the amazing rmsbolt.el, found at 26 | ;; https://gitlab.com/jgkamat/rmsbolt, a package to provide assembly or 27 | ;; bytecode output for a source code input file. 28 | 29 | 30 | ;;; Requires: 31 | 32 | (require 'cl-lib) 33 | (eval-when-compile (require 'subr-x)) 34 | (require 'map) 35 | (require 'compile) 36 | (require 'disass) 37 | (require 'json) 38 | (require 'color) 39 | (require 'pulse) 40 | 41 | ;;; Code: 42 | (defgroup beardbolt nil 43 | "beardbolt customization options" 44 | :group 'applications) 45 | 46 | (defmacro bb--defoption (sym &rest whatever) 47 | `(progn (defcustom ,sym ,@whatever) (put ',sym 'bb--option t))) 48 | 49 | (bb--defoption bb-command nil 50 | "The base command to run beardbolt from." 51 | :type 'string :safe (lambda (v) (or (listp v) (stringp v)))) 52 | (bb--defoption bb-link-flags nil 53 | "Linker flags to pass to linking command." 54 | :type 'string :safe (lambda (v) (or (listp v) (stringp v)))) 55 | (bb--defoption bb-disassemble nil 56 | "Non-nil to assemble then disassemble an output binary." 57 | :type 'boolean :safe 'booleanp) 58 | (bb--defoption bb-asm-format 'att 59 | "Which output assembly format to use. 60 | Passed directly to compiler or disassembler." 61 | :type 'string :safe (lambda (v) (or (null v) (symbolp v) (stringp v)))) 62 | (bb--defoption bb-preserve-directives nil 63 | "Non-nil to keep assembly directives." 64 | :type 'boolean :safe 'booleanp) 65 | (bb--defoption bb-preserve-unused-labels nil 66 | "Non-nil to keep unused labels." 67 | :type 'boolean :safe 'booleanp) 68 | (bb--defoption bb-preserve-library-functions nil 69 | "Non-nil to keep functions with no code related to current file." 70 | :type 'boolean :safe 'booleanp) 71 | (bb--defoption bb-preserve-comments nil 72 | "Non-nil to filter comment-only lines." 73 | :type 'boolean :safe 'booleanp) 74 | (bb--defoption bb-demangle t 75 | "Non-nil to attempt to demangle the resulting assembly." 76 | :type 'boolean :safe 'booleanp) 77 | (bb--defoption bb-execute nil 78 | "Non-nil to run resulting program with these arguments." 79 | :type 'string :safe (lambda (v) (or (null v) (eq t v) (stringp v)))) 80 | (bb--defoption bb-ccj-extra-flags "-DBEARDBOLT" 81 | "Extra flags for compilation command devined from compile_commands.json." 82 | :type 'string :safe (lambda (v) (or (null v) (stringp v)))) 83 | (bb--defoption bb-shuffle-rainbow nil 84 | "Choose less pretty, but potentially more contrasting rainbow colors." 85 | :type 'boolean :safe 'booleanp) 86 | 87 | (defface bb-current-line-face 88 | '((t (:weight bold :inherit highlight))) 89 | "Face to fontify the current line for showing matches.") 90 | 91 | (defvar-local bb--asm-buffer nil) 92 | (defvar-local bb--source-buffer nil) 93 | (defvar-local bb--compile-spec nil) 94 | (defvar-local bb--declared-output nil) 95 | (defvar-local bb--dump-file nil "Temporary file") 96 | (defvar-local bb--line-mappings nil 97 | "List where of asm-to-source mappings. 98 | Each element is ((ASM-BEG-LINE . ASM-END-LINE) . SRC-LINE).") 99 | (defvar-local bb--rainbow-overlays nil "Rainbow overlays.") 100 | 101 | (defun bb--asm-buffer (src-buffer) 102 | "Get/create asm buffer for current source file." 103 | (with-current-buffer src-buffer 104 | (or (and (buffer-live-p bb--asm-buffer) 105 | (equal (buffer-name bb--asm-buffer) "*bb-asm*") 106 | bb--asm-buffer) 107 | (setq bb--asm-buffer 108 | (with-current-buffer 109 | (get-buffer-create "*bb-asm*") 110 | (current-buffer)))))) 111 | 112 | (defvar bb-compile-delay 0.6 113 | "Time in seconds to delay before recompiling if there is a change. 114 | If nil, auto-recompilation is off.") 115 | 116 | (defvar bb--shell "bash" 117 | "Which shell to prefer if available. 118 | Used to work around inconsistencies in alternative shells.") 119 | 120 | (defun bb--sandbox-dir () 121 | (let ((d (expand-file-name "beardbolt-sandbox" user-emacs-directory))) 122 | (make-directory d 'parents) 123 | d)) 124 | 125 | (defvar bb-dir (file-name-directory load-file-name) 126 | "The directory which beardbolt is installed to.") 127 | 128 | (defvar-local bb-objdump-binary "objdump" 129 | "A binary to use for objdumping when using `bb-disassemble'. 130 | Useful if you have multiple objdumpers and want to select between them") 131 | 132 | ;;;; Regexes 133 | 134 | (defvar bb-label-start "^\\([^:]+\\): *\\(?:#\\|$\\)\\(?:.*\\)") 135 | 136 | 137 | (defvar bb-defines-global (rx bol (0+ space) ".glob" 138 | (opt "a") "l" (0+ space) 139 | (group (any ".a-zA-Z_") 140 | (0+ (any "a-zA-Z0-9$_."))))) 141 | (defvar bb-label-reference (rx "." (any "a-zA-Z_") 142 | (0+ 143 | (any "a-zA-Z0-9$_.")))) 144 | (defvar bb-has-opcode (rx bol (1+ space) 145 | (1+ (any "a-zA-Z")))) 146 | 147 | (defvar bb-defines-function-or-object (rx bol 148 | (0+ space) ".type" 149 | (0+ space) 150 | (group (0+ any)) "," (0+ space) (any "@%"))) 151 | (defvar bb-data-defn (rx bol (0+ space) "." 152 | (group (or "string" "asciz" "ascii" 153 | (and 154 | (optional (any "1248")) "byte") 155 | "short" "word" "long" "quad" "value" "zero")))) 156 | 157 | (defvar bb-endblock (rx "." (or "cfi_endproc" "data" "text" "section"))) 158 | (defvar bb-comment-only (rx bol (0+ space) (or (and (or (any "#@;") "//")) 159 | (and "/*" (0+ any) "*/")) 160 | (0+ any) eol)) 161 | (defvar bb-disass-line (rx bol 162 | (group "/" (1+ (not (any ":")))) ":" 163 | (group (1+ num)) 164 | (0+ any))) 165 | (defvar bb-disass-label (rx bol (group (1+ (any digit "a-f"))) 166 | (1+ space) "<" 167 | (group (1+ (not (any ">")))) ">:" eol)) 168 | (defvar bb-disass-dest (rx (0+ any) (group (1+ (any digit "a-f"))) 169 | (1+ space) "<" (group (1+ (not (any ">")))) ">" eol)) 170 | 171 | (defvar bb-disass-opcode (rx bol (0+ space) (group (1+ (any digit "a-f"))) 172 | ":" (0+ space) 173 | (group (1+ 174 | (repeat 2 175 | (any digit "a-f")) 176 | (opt " "))) 177 | (0+ space) 178 | (group (0+ any)))) 179 | (defvar bb-source-file-hint (rx bol (0+ space) ".file" (1+ space) 180 | (group (1+ digit)) (1+ space) ?\" 181 | (group (1+ (not (any ?\")))) ?\" 182 | (opt (1+ space) ?\" 183 | (group (1+ (not (any ?\")))) ?\") 184 | (0+ any))) 185 | (defvar bb-source-tag (rx bol (0+ space) ".loc" (1+ space) 186 | (group (1+ digit)) (1+ space) 187 | (group (1+ digit)) 188 | (0+ any))) 189 | (defvar bb-source-stab (rx bol (0+ any) ".stabn" (1+ space) 190 | (group (1+ digit)) ",0," 191 | (group (1+ digit)) "," (0+ any))) 192 | 193 | (defun bb--split-rm-single (cmd flag &optional test) 194 | "Remove a single FLAG from CMD. Test according to TEST." 195 | (mapconcat #'identity (cl-remove flag (split-string cmd) 196 | :test (or test #'string=)) 197 | " ")) 198 | 199 | (defun bb--split-rm-double (cmd flag) 200 | "Remove a FLAG and subsequent arg from CMD." 201 | (cl-loop while split with split = (split-string cmd) 202 | for i from 0 203 | for probe = (car split) 204 | if (string= probe flag) do (setq split (cddr split)) 205 | else 206 | concat (and (cl-plusp i) " ") 207 | and concat probe and do (setq split (cdr split)))) 208 | 209 | (cl-defun bb--c/c++-setup (&key base-cmd language) 210 | "Get compile specs for gcc/clang." 211 | (let* ((modified-p (buffer-modified-p)) 212 | (source-hint (if modified-p "" (buffer-file-name))) 213 | (base-command (ensure-list (or bb-command 214 | (bb--guess-from-ccj) 215 | base-cmd))) 216 | (cc (car (split-string (car base-command))))) 217 | (cl-labels ((f (x) (expand-file-name x (bb--sandbox-dir))) 218 | (dash-masm-maybe () 219 | (when (string-match 220 | "^\\(gcc\\|g\\+\\+\\|clang\\|clang\\+\\+\\)$" 221 | (file-name-base cc)) 222 | (list (format "-masm=%s" bb-asm-format)))) 223 | (compile (in out) `(,@base-command 224 | "-g1" 225 | "-S" ,@(dash-masm-maybe) 226 | "-o" ,out 227 | ,@(if modified-p 228 | `("-x" ,language "-" "<" ,in) 229 | `(,(shell-quote-argument (buffer-file-name)))))) 230 | (assemble (in out) `("&&" ,cc "-c" ,in "-o" ,out)) 231 | (link (in out) `("&&" ,cc ,@(ensure-list bb-link-flags) ,in "-o" ,out)) 232 | (execute (in) `("&& (" ,in 233 | ,(if (stringp bb-execute) bb-execute "") 234 | "|| true )")) 235 | (disassemble (in out) `("&&" ,bb-objdump-binary "-d" 236 | ,in "--insn-width=16" "-l" 237 | ,(when bb-asm-format (format "-M %s" bb-asm-format)) 238 | ">" ,out))) 239 | `((:compile 240 | ,(lambda (dump-file) 241 | (cons 242 | `(,(compile dump-file (f "beardbolt.s")) 243 | ,@(when bb-execute 244 | `(,(assemble (f "beardbolt.s") (f "beardbolt.o")) 245 | ,(link (f "beardbolt.o") (f "beardbolt.out")) 246 | ,(execute (f "beardbolt.out"))))) 247 | (f "beardbolt.s"))) 248 | ,(lambda (_dump-file) (bb--process-asm source-hint))) 249 | (:compile-assemble-disassemble 250 | ,(lambda (dump-file) 251 | (cons 252 | `(,(compile dump-file (f "beardbolt.s")) 253 | ,(assemble (f "beardbolt.s") (f "beardbolt.o")) 254 | ,(disassemble (f "beardbolt.o") (f "beardbolt.o.disass")) 255 | ,@(when bb-execute 256 | `(,(link (f "beardbolt.o") (f "beardbolt.out")) 257 | ,(execute (f "beardbolt.out"))))) 258 | (f "beardbolt.o.disass"))) 259 | ,(lambda (_dump-file) 260 | (bb--process-disassembled-lines source-hint))))))) 261 | 262 | (cl-defun bb--rust-setup () "Get compile specs for rustc" 263 | (let* ((base (ensure-list (or bb-command "rustc")))) 264 | (cl-labels ((f (x) (expand-file-name x (bb--sandbox-dir))) 265 | (disassemble (in out) `("&&" ,bb-objdump-binary "-d" 266 | ,in "--insn-width=16" "-l" 267 | ,(when bb-asm-format (format "-M %s" bb-asm-format)) 268 | ">" ,out)) 269 | (link (in out) 270 | `(,@base "-C debuginfo=1" "--emit" "link" ,in "-o" ,out)) 271 | (compile (in out) 272 | `(,@base "-C debuginfo=1" "--emit" "asm" ,in 273 | ,(when bb-asm-format (format 274 | "-Cllvm-args=--x86-asm-syntax=%s" 275 | bb-asm-format)) 276 | "-o" ,out))) 277 | `((:compile 278 | ,(lambda (dump-file) 279 | (cons 280 | (compile dump-file (f "beardbolt.s")) 281 | (f "beardbolt.s"))) 282 | ,#'bb--process-asm) 283 | (:compile-assemble-disassemble 284 | ,(lambda (dump-file) 285 | (cons 286 | `(,(link dump-file (f "beardbolt.o")) 287 | ,(disassemble (f "beardbolt.o") (f "beardbolt.o.disass"))) 288 | (f "beardbolt.o.disass"))) 289 | ,#'bb--process-disassembled-lines))))) 290 | 291 | (defvar bb-languages 292 | `((c-mode ,#'bb--c/c++-setup :base-cmd "gcc" :language "c") 293 | (c++-mode ,#'bb--c/c++-setup :base-cmd "g++" :language "c++") 294 | (c-ts-mode ,#'bb--c/c++-setup :base-cmd "gcc" :language "c") 295 | (c++-ts-mode ,#'bb--c/c++-setup :base-cmd "g++" :language "c++") 296 | (rust-mode ,#'bb--rust-setup)) 297 | "Alist of (MAJOR-MODE SETUP . SETUP-ARGS). 298 | 299 | SETUP is a function called with `apply' on SETUP-ARGS. 300 | 301 | It returns a list (SPEC ...) where SPEC is (WHAT CMD-FN PRETTY-FN). 302 | 303 | WHAT is a symbol `:compile' or `:compile-assemble-disassemble'. 304 | 305 | CMD-FN is a function taking IN-FILE, name of the temp file with 306 | the current buffer's content, and returning a cons cell (CMD 307 | . OUT-FILE). CMD is a string or a tree of strings. When 308 | flattened and joined with whitespace, it represents a shell 309 | command to produce a file named OUT-FILE, whose contents are 310 | inserted into the asm buffer. 311 | 312 | PRETTY-FN is a function taking IN-FILE, to run in the asm buffer 313 | where OUT-FILE's contents are freshly inserted. It may clean up 314 | some parts of the buffer and setup a buffer-local value of 315 | `beardbolt--line-mappings' (which see).") 316 | 317 | (defmacro bb--get (sym) `(buffer-local-value ',sym bb--source-buffer)) 318 | 319 | (defmacro bb--sweeping (&rest forms) 320 | (declare (indent 0) 321 | (debug (&rest (form &rest form)))) 322 | (let ((lbp (gensym "lbp-")) (lep (gensym "lep-")) 323 | (preserve-directives (gensym "preserve-directives-")) 324 | (linum (gensym "linum-"))) 325 | `(let ((,preserve-directives (bb--get bb-preserve-directives)) 326 | (,linum 1)) 327 | (goto-char (point-min)) 328 | (while (not (eobp)) 329 | (let ((,lbp (line-beginning-position)) (,lep (line-end-position))) 330 | (cl-macrolet ((match (&rest res) 331 | `(cl-loop for re in ,(cons 'list res) 332 | thereis (re-search-forward re ,',lep t))) 333 | (update-lep () `(setq ,',lep (line-end-position))) 334 | (asm-linum () ',linum) 335 | (preserve () `(progn 336 | (forward-line 1) 337 | (setq ,',linum (1+ ,',linum))))) 338 | (pcase (cond ,@forms) 339 | (:preserve (preserve)) 340 | (:kill (delete-region ,lbp (1+ ,lep))) 341 | (_ 342 | (if ,preserve-directives (preserve) 343 | (delete-region ,lbp (1+ ,lep))))))))))) 344 | 345 | (defun bb--register-mapping (source-linum l) 346 | (let ((current-chunk (car bb--line-mappings))) 347 | (if (and (eq source-linum (cdr current-chunk)) 348 | (eq l (1+ (cdar current-chunk)))) 349 | (setf (cdar current-chunk) l) 350 | (push (cons (cons l l) source-linum) 351 | bb--line-mappings)))) 352 | 353 | (cl-defun bb--process-disassembled-lines (main-file-name) 354 | (let* ((func nil) (source-linum nil)) 355 | (cl-flet ((bb--user-func-p (func) 356 | (let* ((regexp (rx bol (or (and "__" (0+ any)) 357 | (and "_" (or "init" "start" "fini")) 358 | (and (opt "de") "register_tm_clones") 359 | "call_gmon_start" 360 | "frame_dummy" 361 | (and ".plt" (0+ any))) 362 | eol))) 363 | (if regexp (not (string-match-p regexp func)) t)))) 364 | (bb--sweeping 365 | ((match bb-disass-line) 366 | (setq source-linum (and (equal (file-name-base main-file-name) ;; brittle 367 | (file-name-base (match-string 1))) 368 | (string-to-number (match-string 2)))) 369 | :kill) 370 | ((match bb-disass-label) 371 | (setq func (match-string 2)) 372 | (when (bb--user-func-p func) (replace-match (concat func ":"))) 373 | :preserve) 374 | ((and func (not (bb--user-func-p func))) 375 | :kill) 376 | ((match bb-disass-opcode) 377 | (when source-linum 378 | (bb--register-mapping source-linum (asm-linum))) 379 | (replace-match (concat (match-string 1) "\t" (match-string 3))) 380 | :preserve))))) 381 | 382 | (defun bb--process-asm (main-file-name) 383 | (let* ((used-labels (obarray-make)) 384 | (routines (make-hash-table :test #'equal)) 385 | (globals (make-hash-table :test #'equal)) 386 | main-file-tag 387 | main-file-routines 388 | source-linum 389 | current-global 390 | reachable-label 391 | (preserve-comments (bb--get bb-preserve-comments)) 392 | (preserve-unused-labels (bb--get bb-preserve-unused-labels)) 393 | (preserve-library-functions (bb--get bb-preserve-library-functions))) 394 | (bb--sweeping ; first pass 395 | ((not (eq (char-after) ?\t)) 396 | (cond ((match bb-label-start) 397 | (unless (eq :notfound (gethash (match-string 1) globals :notfound)) 398 | (setq current-global (match-string 1))) 399 | :preserve) 400 | (t :kill))) 401 | (t 402 | (cond ((and current-global (match bb-has-opcode)) 403 | (when (eq :notfound (gethash current-global routines :notfound)) 404 | (puthash current-global nil routines)) 405 | (while (match bb-label-reference) 406 | (push (match-string 0) (gethash current-global routines))) 407 | :preserve) 408 | ((and (not preserve-comments) (match bb-comment-only)) 409 | :kill) 410 | ((match bb-defines-global bb-defines-function-or-object) 411 | (puthash (match-string 1) nil globals)) 412 | ((and (match bb-source-file-hint) 413 | (equal (or (match-string 3) (match-string 2)) 414 | main-file-name)) 415 | (setq main-file-tag (match-string 1))) 416 | ((match bb-source-tag) 417 | (when (and current-global 418 | (equal (match-string 1) main-file-tag)) 419 | (push current-global main-file-routines)) 420 | :preserve) 421 | ((match bb-endblock) (setq current-global nil) :preserve) 422 | (t :preserve)))) 423 | (dolist (mfr (if preserve-library-functions 424 | (hash-table-keys routines) 425 | main-file-routines)) 426 | (intern mfr used-labels) 427 | (dolist (callee (gethash mfr routines)) (intern callee used-labels))) 428 | (bb--sweeping ; second pass 429 | ((not (eq (char-after) ?\t)) 430 | (when (match bb-label-start) 431 | (cond 432 | ((intern-soft (match-string 1) used-labels) 433 | (setq reachable-label (match-string 1)) 434 | :preserve) 435 | (t 436 | (if preserve-unused-labels :preserve :kill))))) 437 | (t 438 | (cond ((and (match bb-data-defn) reachable-label) 439 | :preserve) 440 | ((and (match bb-has-opcode) reachable-label) 441 | (when source-linum (bb--register-mapping source-linum (asm-linum))) 442 | :preserve) 443 | ((match bb-source-tag) 444 | (setq source-linum 445 | (and (equal (match-string 1) main-file-tag) 446 | (string-to-number (match-string 2))))) 447 | ((match bb-source-stab) 448 | (pcase (string-to-number (match-string 1)) 449 | ;; http://www.math.utah.edu/docs/info/stabs_11.html 450 | (68 (setq source-linum (string-to-number (match-string 2)))) 451 | ((or 100 132) (setq source-linum nil)))) 452 | ((match bb-endblock) 453 | (setq reachable-label nil))))))) 454 | 455 | (cl-defun bb--rainbowize (src-buffer) 456 | (bb-clear-rainbow-overlays) 457 | (let* ((background-hsl 458 | (ignore-errors 459 | (apply #'color-rgb-to-hsl (color-name-to-rgb (face-background 'default))))) 460 | all-ovs 461 | (idx 0) 462 | total 463 | (shuffle (buffer-local-value 'bb-shuffle-rainbow src-buffer)) 464 | (ht (make-hash-table))) 465 | (cl-loop initially (goto-char (point-min)) 466 | with current-line = 1 467 | for (asm-region . src-line) in bb--line-mappings 468 | for (begl . endl) = asm-region 469 | do (push (cons (progn 470 | (forward-line (- begl current-line)) 471 | (line-beginning-position)) 472 | (progn 473 | (forward-line (- endl begl)) 474 | (setq current-line endl) 475 | (line-end-position))) 476 | (gethash src-line ht))) 477 | ;; The 1+ helps us keep our hue distance from the actual 478 | ;; background color 479 | (setq total (1+ (hash-table-count ht))) 480 | (unless background-hsl (cl-return-from bb--rainbowize nil)) 481 | (maphash 482 | (lambda (src-line asm-pos-regions) 483 | (when (not (zerop src-line)) 484 | (cl-loop 485 | with i = (if shuffle 486 | (mod (* 27 (cl-incf idx)) total) 487 | (cl-incf idx)) 488 | with bright-hsl =(list (mod (+ (cl-first background-hsl) 489 | (/ i (float total))) 490 | 1) 491 | (min (max (cl-second background-hsl) 492 | 0.25) 493 | 0.8) 494 | (min (max (cl-third background-hsl) 495 | 0.25) 496 | 0.8)) 497 | with muted-hsl = (list (car bright-hsl) 498 | (/ (cadr bright-hsl) 2.0) 499 | (caddr bright-hsl)) 500 | with color = (apply #'color-rgb-to-hex (apply #'color-hsl-to-rgb bright-hsl)) 501 | with muted-color = (apply #'color-rgb-to-hex (apply #'color-hsl-to-rgb muted-hsl)) 502 | for (beg . end) in asm-pos-regions 503 | for asm-ov = (make-overlay beg end) 504 | do 505 | (overlay-put asm-ov 'priority 0) 506 | (push asm-ov all-ovs) 507 | (overlay-put asm-ov 'face `(:background ,color)) 508 | (overlay-put asm-ov 'beardbolt-rainbow-face `(:background ,color)) 509 | (overlay-put asm-ov 'beardbolt-muted-face `(:background ,muted-color)) 510 | (overlay-put asm-ov 'beardbolt 'asm) 511 | collect asm-ov into this-lines-asm-overlays 512 | finally 513 | (with-current-buffer src-buffer 514 | (save-excursion 515 | (goto-char (point-min)) 516 | (forward-line (1- src-line)) 517 | (let* ((ov (make-overlay (line-beginning-position) 518 | (1+ (line-end-position)))) 519 | (group (cons ov this-lines-asm-overlays))) 520 | (overlay-put ov 'beardbolt-related-overlays group) 521 | (dolist (o group) 522 | (overlay-put o 'beardbolt-related-overlays group)) 523 | (overlay-put ov 'face `(:background ,color)) 524 | (overlay-put ov 'beardbolt-rainbow-face `(:background ,color)) 525 | (overlay-put ov 'beardbolt-muted-face `(:background ,muted-color)) 526 | (overlay-put ov 'beardbolt t) 527 | (push ov all-ovs))))))) 528 | ht) 529 | (mapc #'delete-overlay bb--rainbow-overlays) 530 | (setq bb--rainbow-overlays all-ovs))) 531 | 532 | (cl-defmacro bb--when-live-buffer (buf &rest body) 533 | "Check BUF live, then do BODY in it." (declare (indent 1) (debug t)) 534 | (let ((b (gensym))) 535 | `(let ((,b ,buf)) (if (buffer-live-p ,b) (with-current-buffer ,b ,@body))))) 536 | 537 | (defun bb-clear-rainbow-overlays () 538 | "Clear rainbow from the source file and asm output." 539 | (interactive) 540 | (let ((output-buffer (if (eq major-mode 'bb--asm-mode) 541 | (current-buffer) 542 | bb--asm-buffer))) 543 | (bb--when-live-buffer output-buffer 544 | (bb--when-live-buffer bb--source-buffer 545 | (save-restriction 546 | (widen) 547 | (cl-loop for o in (overlays-in (point-min) (point-max)) 548 | when (overlay-get o 'beardbolt) do (delete-overlay o))))) 549 | (mapc #'delete-overlay bb--rainbow-overlays) 550 | (setq bb--rainbow-overlays nil))) 551 | 552 | ;;;;; Handlers 553 | (cl-defun bb--handle-finish-compile (compilation-buffer str) 554 | "Finish hook for compilations. Runs in buffer COMPILATION-BUFFER. 555 | Argument STR compilation finish status." 556 | (let* ((dump-file-name bb--dump-file) 557 | (src-buffer bb--source-buffer) 558 | (origin-window (or (get-buffer-window src-buffer) 559 | (selected-window))) 560 | (compile-spec bb--compile-spec) 561 | (declared-output bb--declared-output) 562 | (asm-buffer (bb--asm-buffer src-buffer))) 563 | (delete-file dump-file-name) 564 | (with-current-buffer asm-buffer 565 | (bb--asm-mode) 566 | (setq bb--source-buffer src-buffer) 567 | (let* ((inhibit-modification-hooks t) 568 | (inhibit-read-only t) 569 | (window 570 | (with-selected-window origin-window 571 | (display-buffer asm-buffer '(nil (inhibit-same-window . t)))))) 572 | (erase-buffer) 573 | (cond 574 | ((string-match "^finished" str) 575 | (mapc #'delete-overlay (overlays-in (point-min) (point-max))) 576 | (insert-file-contents declared-output) 577 | (setq bb--line-mappings nil) 578 | (save-excursion (funcall (cadr compile-spec) dump-file-name)) 579 | (setq bb--line-mappings (reverse bb--line-mappings)) 580 | (when (bb--get bb-demangle) 581 | (shell-command-on-region (point-min) (point-max) "c++filt" 582 | (current-buffer) 'no-mark)) 583 | (bb--rainbowize src-buffer)) 584 | (t 585 | (insert ""))) 586 | (unless (or (string-match "^interrupt" str) 587 | (get-buffer-window compilation-buffer) 588 | (and (string-match "^finished" str) 589 | (not (bb--get bb-execute)))) 590 | (with-selected-window window 591 | (let ((cwindow 592 | (display-buffer compilation-buffer 593 | `((display-buffer-below-selected))))) 594 | (set-window-dedicated-p cwindow 'bb-dedication)))))))) 595 | 596 | (defun bb--compilation-buffer (&rest _) 597 | (get-buffer-create "*bb-compilation*")) 598 | 599 | ;;;;; UI Functions 600 | (defun bb-compile (lang-desc) 601 | "Run beardbolt on current buffer for LANG-DESC. 602 | LANG-DESC is an element of `beardbolt-languages'. Interactively, 603 | determine LANG from `major-mode'." 604 | (interactive (list (assoc major-mode bb-languages))) 605 | (bb--maybe-stop-running-compilation) 606 | (mapatoms (lambda (s) (when (get s 'bb--option) (kill-local-variable s)))) 607 | (cl-letf (((symbol-function 'hack-local-variables-confirm) 608 | (lambda (_all-vars unsafe-vars risky-vars &rest _) 609 | (when unsafe-vars 610 | (message "[beardbolt] Some variables unsafe %s" unsafe-vars)) 611 | (when risky-vars 612 | (message "[beardbolt] Some variables risky %s" risky-vars))))) 613 | (hack-local-variables)) 614 | (let* ((dump-file (make-temp-file "beardbolt-dump-" nil 615 | (concat "." (file-name-extension buffer-file-name)))) 616 | (src-buffer (current-buffer)) 617 | (specs (apply (cadr lang-desc) (cddr lang-desc))) 618 | (spec (alist-get 619 | (if bb-disassemble :compile-assemble-disassemble :compile) 620 | specs)) 621 | (command-and-declared-output (funcall (car spec) dump-file)) 622 | (cmd (car command-and-declared-output)) 623 | (cmd (mapconcat 624 | (lambda (s) (if (stringp s) s 625 | (mapconcat #'identity (flatten-list s) " "))) 626 | (ensure-list cmd) " \\\n"))) 627 | (let ((inhibit-message t)) 628 | (write-region (point-min) (point-max) dump-file)) 629 | (with-current-buffer ; With compilation buffer 630 | (let ((shell-file-name (or (executable-find bb--shell) 631 | shell-file-name)) 632 | (compilation-auto-jump-to-first-error t)) 633 | ;; TODO should this be configurable? 634 | (compilation-start cmd nil #'bb--compilation-buffer)) 635 | ;; Only jump to errors, skip over warnings 636 | (setq-local compilation-skip-threshold 2) 637 | (setq-local compilation-always-kill t) 638 | (setq-local inhibit-message t) 639 | (add-hook 'compilation-finish-functions #'bb--handle-finish-compile nil t) 640 | (setq bb--source-buffer src-buffer) 641 | (setq bb--compile-spec spec) 642 | (setq bb--dump-file dump-file) 643 | (setq bb--declared-output (cdr command-and-declared-output))))) 644 | 645 | (defun bb--maybe-stop-running-compilation () 646 | (let ((buffer (bb--compilation-buffer))) 647 | (when-let* ((proc (get-buffer-process buffer))) 648 | (set-process-query-on-exit-flag proc nil) 649 | (interrupt-process proc)))) 650 | 651 | ;;;; Keymap 652 | (defvar bb-mode-map 653 | (let ((map (make-sparse-keymap))) 654 | (define-key map (kbd "C-c C-c") #'bb-compile) 655 | (define-key map (kbd "C-c C-d") #'bb-clear-rainbow-overlays) 656 | map) 657 | "Keymap for function `bb-mode'.") 658 | 659 | ;;;;; Starter Definitions 660 | 661 | (defvar bb-starter-files 662 | '(("c++" . "beardbolt.cpp") 663 | ("c" . "beardbolt.c") 664 | ("rust" . "beardbolt.rs"))) 665 | 666 | ;;;###autoload 667 | (defun beardbolt-starter (lang-name) 668 | "Setup new sandbox file for experiments. 669 | With prefix argument, choose from starter files in `bb-starter-files'." 670 | (interactive 671 | (list (if current-prefix-arg 672 | (completing-read "Language: " bb-starter-files nil t) 673 | (caar bb-starter-files)))) 674 | (let* ((starter-file-name (cdr (assoc lang-name bb-starter-files))) 675 | (base (file-name-base starter-file-name)) 676 | (ext (file-name-extension starter-file-name)) 677 | (sandbox-file 678 | (expand-file-name (concat base "-" 679 | (format-time-string "%FT%T%z") 680 | "." ext) 681 | (bb--sandbox-dir))) 682 | (src-file-name 683 | (when bb-dir 684 | (expand-file-name starter-file-name 685 | (expand-file-name "starters/" bb-dir)))) 686 | (src-file-exists (when src-file-name 687 | (file-exists-p src-file-name)))) 688 | (if (not src-file-exists) 689 | (error "Could not find starter files!") 690 | (unless (file-exists-p sandbox-file) 691 | (copy-file src-file-name sandbox-file)) 692 | (find-file sandbox-file) 693 | (bb-mode 1)))) 694 | 695 | (defun bb--recenter-maybe (ov) 696 | (bb--when-live-buffer (overlay-buffer ov) 697 | (cl-loop with pos = (overlay-start ov) 698 | for w in (cl-remove-if (lambda (w) 699 | (and (>= pos (* 1.1 (window-start w))) 700 | (<= pos (* 0.9 (window-end w))))) 701 | (get-buffer-window-list)) 702 | unless (eq w (selected-window)) 703 | do (set-window-point w pos) 704 | (with-selected-window w (recenter))))) 705 | 706 | (defvar bb--currently-synched-overlays nil) 707 | 708 | (defun bb--synch-relation-overlays () 709 | (let* ((at-point (overlays-at (point))) 710 | (all-ovs (if (eq major-mode 'bb--asm-mode) 711 | bb--rainbow-overlays 712 | (and bb--asm-buffer 713 | (buffer-local-value 'bb--rainbow-overlays bb--asm-buffer)))) 714 | (ov (cl-find-if (lambda (ov) (overlay-get ov 'beardbolt-rainbow-face)) 715 | at-point))) 716 | (cond ((and ov (not (member ov bb--currently-synched-overlays))) 717 | (dolist (o all-ovs) 718 | (overlay-put o 'face (overlay-get o 'beardbolt-muted-face))) 719 | (setq bb--currently-synched-overlays 720 | (overlay-get ov 'beardbolt-related-overlays)) 721 | (setq bb--currently-synched-overlays 722 | (cl-sort bb--currently-synched-overlays #'< :key #'overlay-start)) 723 | (dolist (o bb--currently-synched-overlays) 724 | (overlay-put o 'face 'bb-current-line-face)) 725 | (let* ((other-buffer-overlays 726 | (cl-remove (current-buffer) 727 | bb--currently-synched-overlays 728 | :key #'overlay-buffer)) 729 | (recenter-target (car other-buffer-overlays)) 730 | (pulse-delay 0.01) 731 | (asm-overlays 732 | (cl-remove-if-not (lambda (ov) 733 | (eq 'asm (overlay-get ov 'beardbolt))) 734 | bb--currently-synched-overlays))) 735 | (if (memq recenter-target asm-overlays) 736 | (message "[beardbolt] maps to %s asm regions." 737 | (length asm-overlays)) 738 | (message "[beardbolt] asm region %s/%s for source line %s." 739 | (1+ (cl-position ov asm-overlays)) 740 | (length asm-overlays) 741 | (with-current-buffer (overlay-buffer recenter-target) 742 | (line-number-at-pos (overlay-start recenter-target))))) 743 | (bb--recenter-maybe recenter-target) 744 | (pulse-momentary-highlight-overlay recenter-target 745 | 'bb-current-line-face))) 746 | ((not ov) 747 | (dolist (o all-ovs) 748 | (overlay-put o 'face (overlay-get o 'beardbolt-rainbow-face))) 749 | (setq bb--currently-synched-overlays nil))))) 750 | 751 | (defvar bb--change-timer nil) 752 | 753 | (defun bb--after-change (&rest _) 754 | (bb-clear-rainbow-overlays) 755 | (when bb-compile-delay 756 | (when (timerp bb--change-timer) (cancel-timer bb--change-timer)) 757 | (setq bb--change-timer 758 | (run-with-timer bb-compile-delay nil #'bb-compile 759 | (assoc major-mode bb-languages))))) 760 | 761 | (defun bb--guess-from-ccj () 762 | (if-let* ((ccj-basename "compile_commands.json") 763 | (ccj-dir (locate-dominating-file default-directory ccj-basename)) 764 | (ccj-file (expand-file-name ccj-basename ccj-dir)) 765 | (ccj (with-temp-buffer 766 | (insert-file-contents ccj-file) 767 | (goto-char (point-min)) 768 | (json-parse-buffer :object-type 'plist))) 769 | (cmd (cl-loop for e across ccj 770 | for file = (plist-get e :file) 771 | when (equal file buffer-file-name) 772 | return (plist-get e :command))) 773 | (cmd (bb--split-rm-double cmd "-o")) 774 | (cmd (bb--split-rm-double cmd "-c")) 775 | (cmd (bb--split-rm-single cmd "-flto" #'string-prefix-p))) 776 | (list cmd bb-ccj-extra-flags))) 777 | 778 | ;;;###autoload 779 | (define-minor-mode beardbolt-mode 780 | "Toggle `beardbolt-mode'. May be enabled by user in source buffer." 781 | :global nil :lighter " ⚡" :keymap bb-mode-map 782 | (cond 783 | (bb-mode 784 | (add-hook 'after-change-functions #'bb--after-change nil t) 785 | (add-hook 'post-command-hook #'bb--synch-relation-overlays nil t)) 786 | (t 787 | (remove-hook 'after-change-functions #'bb--after-change t) 788 | (remove-hook 'post-command-hook #'bb--synch-relation-overlays t)))) 789 | 790 | (define-derived-mode bb--asm-mode asm-mode "⚡asm ⚡" 791 | "Toggle `bearbolt--output-mode', internal mode for asm buffers." 792 | (add-hook 'kill-buffer-hook #'bb-clear-rainbow-overlays nil t) 793 | (add-hook 'post-command-hook #'bb--synch-relation-overlays nil t) 794 | (setq truncate-lines t) 795 | (read-only-mode t) 796 | (buffer-disable-undo) 797 | (local-set-key (kbd "q") 'quit-window)) 798 | 799 | (provide 'beardbolt) 800 | 801 | ;;; beardbolt.el ends here 802 | ;; Local Variables: 803 | ;; read-symbol-shorthands: (("bb-" . "beardbolt-")) 804 | ;; End: 805 | -------------------------------------------------------------------------------- /beardbolt.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/joaotavora/beardbolt/828fbddeb3145139f9d7eca4336f7c970a331d9d/beardbolt.gif -------------------------------------------------------------------------------- /starters/beardbolt.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | 5 | int main(int argc, char *argv[]) { 6 | if(argc == 2) { 7 | printf("Checking License: %s\n", argv[1]); 8 | 9 | double hash_value = 0; 10 | for(int i = 0; i < strlen(argv[1]); i++) { 11 | hash_value += sin(argv[1][i]) * pow(2.3765, i % 5); 12 | } 13 | 14 | if(fabs(hash_value - 42.0) < 0.01) { 15 | printf("Access Granted!\n"); 16 | } else { 17 | printf("WRONG! (Hash: %.2f)\n", hash_value); 18 | } 19 | } else { 20 | printf("Usage: %s \n", argv[0]); 21 | } 22 | return 0; 23 | } 24 | 25 | /* Local Variables: */ 26 | /* beardbolt-command: "gcc -O3" */ 27 | /* beardbolt-demangle: t */ 28 | /* beardbolt-execute: "DEAD-BEEF-42-OK" */ 29 | /* beardbolt-link-flags: "-lm" */ 30 | /* beardbolt-disassemble: nil */ 31 | /* beardbolt-preserve-library-functions: nil */ 32 | /* beardbolt-preserve-unused-labels: nil */ 33 | /* beardbolt-preserve-directives: nil */ 34 | /* End: */ 35 | -------------------------------------------------------------------------------- /starters/beardbolt.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | 5 | template 6 | void bubble(It from, It to) { 7 | for (auto i = from; i < to - 1; i++) 8 | for (auto j = to - 1; i < j; j--) 9 | if (*j < *(j - 1)) 10 | std::swap(*j, *(j - 1)); 11 | } 12 | 13 | int main(int argc, char* argv[]) { 14 | std::vector vi; 15 | std::transform(argv, argv+argc, std::back_inserter(vi), 16 | std::atoi); 17 | bubble(vi.begin(), vi.end()); 18 | 19 | std::cout << "Sorted array : \n"; 20 | for (auto&& e : vi) std::cout << e << "\n"; 21 | return 0; 22 | } 23 | 24 | // Local Variables: 25 | // beardbolt-command: "g++ -std=c++20 -O3" 26 | // beardbolt-demangle: t 27 | // beardbolt-execute: "5 4 blargh 2 1" 28 | // beardbolt-link-flags: "-fsanitize=address" 29 | // beardbolt-disassemble: nil 30 | // beardbolt-preserve-library-functions: nil 31 | // beardbolt-preserve-unused-labels: nil 32 | // beardbolt-preserve-directives: nil 33 | // End: 34 | -------------------------------------------------------------------------------- /starters/beardbolt.rs: -------------------------------------------------------------------------------- 1 | fn is_rms(a: char) -> i32 { 2 | match a { 3 | 'R' => 1, 4 | 'M' => 2, 5 | 'S' => 3, 6 | _ => 0, 7 | } 8 | } 9 | 10 | fn main() { 11 | let a: u8 = 1 + 1; 12 | if is_rms(a as char) != 0 { 13 | println!("{}", a); 14 | }; 15 | 42; 16 | } 17 | // rust beardbolt starter file 18 | 19 | // Local Variables: 20 | // beardbolt-command: "rustc -C opt-level=0" 21 | // beardbolt-preserve-library-functions: nil 22 | // beardbolt-demangle: t 23 | // beardbolt-disassemble: nil 24 | // End: 25 | 26 | 27 | -------------------------------------------------------------------------------- /starters/slow-to-process.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | #include 5 | #include 6 | #include 7 | #include 8 | #include 9 | #include 10 | #include 11 | 12 | #include 13 | #include 14 | #include 15 | #include 16 | #include 17 | #include 18 | 19 | template class Set; 20 | 21 | template class List : public std::vector { 22 | typedef std::vector Base; 23 | 24 | public: 25 | static const size_t npos = std::string::npos; 26 | explicit List(size_t count, T &&defaultValue = T()) 27 | : Base(count, std::move(defaultValue)) {} 28 | 29 | explicit List() : Base() {} 30 | 31 | template 32 | List(const std::vector &other) : Base(other.size(), T()) { 33 | const size_t len = other.size(); 34 | for (size_t i = 0; i < len; ++i) { 35 | std::vector::operator[](i) = other.at(i); 36 | } 37 | } 38 | 39 | template 40 | List(std::vector &&other) : Base(other.size(), T()) { 41 | const size_t len = other.size(); 42 | for (size_t i = 0; i < len; ++i) { 43 | std::vector::operator[](i) = std::move(other.at(i)); 44 | } 45 | } 46 | 47 | List(std::initializer_list list) : Base(list) {} 48 | 49 | List(typename Base::const_iterator f, typename Base::const_iterator l) 50 | : Base(f, l) {} 51 | 52 | bool contains(const T &t) const { 53 | return std::find(Base::begin(), Base::end(), t) != Base::end(); 54 | } 55 | 56 | bool isEmpty() const { return Base::empty(); } 57 | 58 | void append(const T &t) { Base::push_back(t); } 59 | 60 | void prepend(const T &t) { Base::insert(Base::begin(), t); } 61 | 62 | void append(T &&t) { Base::push_back(std::forward(t)); } 63 | 64 | void prepend(T &&t) { Base::insert(Base::begin(), std::forward(t)); } 65 | 66 | void append(const List &t) { 67 | const size_t len = t.size(); 68 | for (size_t i = 0; i < len; ++i) 69 | Base::push_back(t.at(i)); 70 | } 71 | 72 | void insert(size_t idx, const List &list) { 73 | Base::insert(Base::begin() + idx, list.begin(), list.end()); 74 | } 75 | 76 | void insert(size_t idx, const T &val) { 77 | Base::insert(Base::begin() + idx, val); 78 | } 79 | 80 | void sort() { std::sort(Base::begin(), Base::end()); } 81 | 82 | void sort(std::function func) { 83 | std::sort(Base::begin(), Base::end(), func); 84 | } 85 | 86 | size_t indexOf(const T &t) const { 87 | const typename Base::const_iterator beg = Base::begin(); 88 | const typename Base::const_iterator end = Base::end(); 89 | const typename Base::const_iterator it = std::find(beg, end, t); 90 | return it == end ? npos : (it - beg); 91 | } 92 | 93 | size_t lastIndexOf(const T &t, int from = -1) const { 94 | const size_t s = size(); 95 | if (!s) 96 | return npos; 97 | if (from < 0) { 98 | from += s; 99 | } 100 | from = std::min(s - 1, from); 101 | if (from >= 0) { 102 | const T *haystack = Base::constData(); 103 | const T *needle = haystack + from + 1; 104 | while (needle != haystack) { 105 | if (*--needle == t) 106 | return needle - haystack; 107 | } 108 | } 109 | return npos; 110 | } 111 | 112 | size_t remove(const T &t) { 113 | size_t ret = 0; 114 | while (true) { 115 | typename Base::iterator it = std::find(Base::begin(), Base::end(), t); 116 | if (it == Base::end()) 117 | break; 118 | Base::erase(it); 119 | ++ret; 120 | } 121 | return ret; 122 | } 123 | 124 | void removeAt(size_t idx) { Base::erase(Base::begin() + idx); } 125 | 126 | void remove(size_t idx, size_t count) { 127 | Base::erase(Base::begin() + idx, Base::begin() + idx + count); 128 | } 129 | 130 | void removeLast() { Base::pop_back(); } 131 | 132 | void removeFirst() { Base::erase(Base::begin()); } 133 | 134 | size_t size() const { return Base::size(); } 135 | 136 | T value(size_t idx, const T &defaultValue) const { 137 | return idx < Base::size() ? Base::at(idx) : defaultValue; 138 | } 139 | 140 | T value(size_t idx) const { return idx < Base::size() ? Base::at(idx) : T(); } 141 | 142 | void deleteAll() { 143 | typename Base::iterator it = Base::begin(); 144 | while (it != Base::end()) { 145 | delete *it; 146 | ++it; 147 | } 148 | Base::clear(); 149 | } 150 | 151 | void deleteAll(void (*deleter)(void *t)) { 152 | typename Base::iterator it = Base::begin(); 153 | while (it != Base::end()) { 154 | deleter(*it); 155 | ++it; 156 | } 157 | Base::clear(); 158 | } 159 | 160 | void chop(size_t count) { 161 | assert(count <= size()); 162 | Base::resize(size() - count); 163 | } 164 | 165 | size_t truncate(size_t count) { 166 | const size_t s = size(); 167 | if (s > count) { 168 | Base::resize(count); 169 | return s - count; 170 | } 171 | return 0; 172 | } 173 | 174 | Set toSet() const; // implemented in Set.h 175 | 176 | T &first() { return Base::operator[](0); } 177 | 178 | const T &first() const { return Base::at(0); } 179 | 180 | T takeFirst() { 181 | const T ret = first(); 182 | removeFirst(); 183 | return ret; 184 | } 185 | 186 | T &last() { return Base::operator[](size() - 1); } 187 | 188 | T takeLast() { 189 | const T ret = last(); 190 | removeLast(); 191 | return ret; 192 | } 193 | 194 | const T &last() const { return Base::at(size() - 1); } 195 | 196 | List mid(size_t from, int len = -1) const { 197 | assert(from >= 0); 198 | const size_t count = Base::size(); 199 | if (from >= count) 200 | return List(); 201 | if (len < 0) { 202 | len = count - from; 203 | } else { 204 | len = std::min(count - from, len); 205 | } 206 | return List(Base::begin() + from, Base::begin() + from + len); 207 | } 208 | 209 | bool startsWith(const List &t) const { 210 | if (size() < t.size()) 211 | return false; 212 | for (size_t i = 0; i < t.size(); ++i) { 213 | if (Base::at(i) != t.Base::at(i)) 214 | return false; 215 | } 216 | return true; 217 | } 218 | 219 | List operator+(const T &t) const { 220 | const size_t s = Base::size(); 221 | List ret(s + 1); 222 | for (size_t i = 0; i < s; ++i) 223 | ret[i] = Base::at(i); 224 | ret[s] = t; 225 | return ret; 226 | } 227 | 228 | List operator+(const List &t) const { 229 | if (t.isEmpty()) 230 | return *this; 231 | 232 | size_t s = Base::size(); 233 | List ret(s + t.size()); 234 | 235 | for (size_t i = 0; i < s; ++i) 236 | ret[i] = Base::at(i); 237 | 238 | for (typename List::const_iterator it = t.begin(); it != t.end(); ++it) 239 | ret[s++] = *it; 240 | 241 | return ret; 242 | } 243 | 244 | template int compare(const List &other) const { 245 | const size_t me = size(); 246 | const size_t him = other.size(); 247 | if (me < him) { 248 | return -1; 249 | } else if (me > him) { 250 | return 1; 251 | } 252 | typename List::const_iterator bit = other.begin(); 253 | for (typename List::const_iterator it = Base::begin(); it != Base::end(); 254 | ++it) { 255 | const int cmp = it->compare(*bit); 256 | if (cmp) 257 | return cmp; 258 | ++bit; 259 | } 260 | return 0; 261 | } 262 | 263 | size_t remove(std::function match) { 264 | size_t ret = 0; 265 | typename Base::iterator it = Base::begin(); 266 | while (it != Base::end()) { 267 | if (match(*it)) { 268 | it = Base::erase(it); 269 | ++ret; 270 | } else { 271 | ++it; 272 | } 273 | } 274 | return ret; 275 | } 276 | 277 | typename std::vector::const_iterator constBegin() const { 278 | return std::vector::begin(); 279 | } 280 | 281 | typename std::vector::const_iterator constEnd() const { 282 | return std::vector::end(); 283 | } 284 | 285 | template bool operator==(const List &other) const { 286 | return !compare(other); 287 | } 288 | 289 | template bool operator!=(const List &other) const { 290 | return compare(other); 291 | } 292 | 293 | template bool operator<(const List &other) const { 294 | return compare(other) < 0; 295 | } 296 | 297 | template bool operator>(const List &other) const { 298 | return compare(other) > 0; 299 | } 300 | 301 | List &operator+=(const T &t) { 302 | append(t); 303 | return *this; 304 | } 305 | 306 | List &operator+=(const List &t) { 307 | append(t); 308 | return *this; 309 | } 310 | 311 | List &operator<<(const T &t) { 312 | append(t); 313 | return *this; 314 | } 315 | 316 | List &operator<<(const List &t) { 317 | append(t); 318 | return *this; 319 | } 320 | }; 321 | 322 | #define RCT_PRINTF_WARNING(fmt, firstarg) \ 323 | __attribute__((__format__(__printf__, fmt, firstarg))) 324 | 325 | template class String { 326 | public: 327 | static const size_t npos = std::string::npos; 328 | typedef size_t size_type; 329 | 330 | enum CaseSensitivity { CaseSensitive, CaseInsensitive }; 331 | String(const char *ch = 0, size_t len = npos) { 332 | if (ch) { 333 | if (len == npos) 334 | len = strlen(ch); 335 | mString.assign(ch, len); 336 | } 337 | } 338 | String(const char *start, const char *end) { 339 | if (start) { 340 | mString.assign(start, end); 341 | } 342 | } 343 | String(size_t len, char fillChar) : mString(len, fillChar) {} 344 | 345 | String(const String &ba) : mString(ba.mString) {} 346 | 347 | String(String &&ba) : mString(std::move(ba.mString)) {} 348 | 349 | String(const std::string &str) : mString(str) {} 350 | 351 | String(std::string &&str) : mString(std::move(str)) {} 352 | 353 | String &operator=(const String &other) { 354 | mString = other.mString; 355 | return *this; 356 | } 357 | 358 | String &operator=(String &&other) { 359 | mString = std::move(other.mString); 360 | return *this; 361 | } 362 | 363 | void assign(const char *ch, size_t len = npos) { 364 | if (ch || !len) { 365 | if (len == npos) 366 | len = strlen(ch); 367 | mString.assign(ch, len); 368 | } else { 369 | clear(); 370 | } 371 | } 372 | 373 | typedef std::string::const_iterator const_iterator; 374 | typedef std::string::iterator iterator; 375 | 376 | const_iterator begin() const { return mString.begin(); } 377 | const_iterator end() const { return mString.end(); } 378 | iterator begin() { return mString.begin(); } 379 | iterator end() { return mString.end(); } 380 | 381 | size_t lastIndexOf(char ch, size_t from = npos, 382 | CaseSensitivity cs = CaseSensitive) const { 383 | if (cs == CaseSensitive) 384 | return mString.rfind(ch, from == npos ? std::string::npos : size_t(from)); 385 | const char *str = mString.c_str(); 386 | if (from == npos) 387 | from = mString.size() - 1; 388 | ch = tolower(ch); 389 | int f = static_cast(from); 390 | while (f >= 0) { 391 | if (tolower(str[f]) == ch) 392 | return from; 393 | --f; 394 | } 395 | return npos; 396 | } 397 | 398 | size_t indexOf(char ch, size_t from = 0, 399 | CaseSensitivity cs = CaseSensitive) const { 400 | if (cs == CaseSensitive) 401 | return mString.find(ch, from); 402 | const char *data = mString.c_str(); 403 | ch = tolower(ch); 404 | const size_t len = mString.size(); 405 | while (from < len) { 406 | if (tolower(data[from]) == ch) 407 | return from; 408 | ++from; 409 | } 410 | return npos; 411 | } 412 | 413 | size_t lastIndexOf(const char *ch, size_t len, size_t from = npos, 414 | CaseSensitivity cs = CaseSensitive) const { 415 | switch (len) { 416 | case 0: 417 | return npos; 418 | case 1: 419 | return lastIndexOf(*ch, from, cs); 420 | default: { 421 | if (cs == CaseSensitive) 422 | return mString.rfind(ch, from, len); 423 | if (from == npos) 424 | from = mString.size() - 1; 425 | String lowered(ch, len); 426 | lowered.lowerCase(); 427 | const size_t needleSize = lowered.size(); 428 | size_t matched = 0; 429 | int f = static_cast(from); 430 | while (f >= 0) { 431 | if (lowered.at(needleSize - matched - 1) != tolower(at(f))) { 432 | matched = 0; 433 | } else if (++matched == needleSize) { 434 | return f; 435 | } 436 | 437 | --f; 438 | } 439 | break; 440 | } 441 | } 442 | return npos; 443 | } 444 | 445 | size_t indexOf(const char *ch, size_t len, size_t from = 0, 446 | CaseSensitivity cs = CaseSensitive) const { 447 | switch (len) { 448 | case 0: 449 | return npos; 450 | case 1: 451 | return indexOf(*ch, from, cs); 452 | default: { 453 | if (cs == CaseSensitive) 454 | return mString.find(ch, from, len); 455 | 456 | String lowered(ch, len); 457 | lowered.lowerCase(); 458 | const size_t count = size(); 459 | size_t matched = 0; 460 | 461 | for (size_t i = from; i < count; ++i) { 462 | if (lowered.at(matched) != tolower(at(i))) { 463 | matched = 0; 464 | } else if (++matched == lowered.size()) { 465 | return i - matched + 1; 466 | } 467 | } 468 | break; 469 | } 470 | } 471 | return npos; 472 | } 473 | 474 | bool contains(const String &other, CaseSensitivity cs = CaseSensitive) const { 475 | return indexOf(other, 0, cs) != npos; 476 | } 477 | 478 | bool contains(char ch, CaseSensitivity cs = CaseSensitive) const { 479 | return indexOf(ch, 0, cs) != npos; 480 | } 481 | 482 | size_t chomp(const String &chars) { 483 | size_t idx = size() - 1; 484 | while (idx > 0) { 485 | if (chars.contains(at(idx - 1))) { 486 | --idx; 487 | } else { 488 | break; 489 | } 490 | } 491 | const size_t ret = size() - idx - 1; 492 | if (ret) 493 | resize(idx); 494 | return ret; 495 | } 496 | 497 | size_t chomp(char ch) { return chomp(String(&ch, 1)); } 498 | 499 | size_t lastIndexOf(const String &ba, size_t from = npos, 500 | CaseSensitivity cs = CaseSensitive) const { 501 | return lastIndexOf(ba.constData(), ba.size(), from, cs); 502 | } 503 | 504 | size_t indexOf(const String &ba, size_t from = 0, 505 | CaseSensitivity cs = CaseSensitive) const { 506 | return indexOf(ba.constData(), ba.size(), from, cs); 507 | } 508 | 509 | char first() const { return at(0); } 510 | 511 | char &first() { return operator[](0); } 512 | 513 | char last() const { 514 | assert(!isEmpty()); 515 | return at(size() - 1); 516 | } 517 | 518 | char &last() { 519 | assert(!isEmpty()); 520 | return operator[](size() - 1); 521 | } 522 | 523 | void lowerCase() { 524 | std::transform(mString.begin(), mString.end(), mString.begin(), ::tolower); 525 | } 526 | 527 | String toLower() const { 528 | std::string ret = mString; 529 | std::transform(ret.begin(), ret.end(), ret.begin(), ::tolower); 530 | return ret; 531 | } 532 | 533 | String toUpper() const { 534 | std::string ret = mString; 535 | std::transform(ret.begin(), ret.end(), ret.begin(), ::toupper); 536 | return ret; 537 | } 538 | 539 | void upperCase() { 540 | std::transform(mString.begin(), mString.end(), mString.begin(), ::toupper); 541 | } 542 | 543 | String trimmed(const String &trim = " \f\n\r\t\v") const { 544 | const size_t start = mString.find_first_not_of(trim); 545 | if (start == npos) 546 | return String(); 547 | 548 | const size_t end = mString.find_last_not_of(trim); 549 | assert(end != npos); 550 | return mid(start, end - start + 1); 551 | } 552 | 553 | enum Pad { Beginning, End }; 554 | String padded(Pad pad, size_t len, char fillChar = ' ', 555 | bool trunc = false) const { 556 | const size_t l = length(); 557 | if (l == len) { 558 | return *this; 559 | } else if (l > len) { 560 | if (!trunc) 561 | return *this; 562 | if (pad == Beginning) { 563 | return right(len); 564 | } else { 565 | return left(len); 566 | } 567 | } else { 568 | String ret = *this; 569 | if (pad == Beginning) { 570 | ret.prepend(String(len - l, fillChar)); 571 | } else { 572 | ret.append(String(len - l, fillChar)); 573 | } 574 | return ret; 575 | } 576 | } 577 | 578 | char *data() { return &mString[0]; } 579 | 580 | void clear() { mString.clear(); } 581 | const char *data() const { return mString.data(); } 582 | bool isEmpty() const { return mString.empty(); } 583 | 584 | char at(size_t i) const { return mString.at(i); } 585 | 586 | char &operator[](size_t i) { return mString.operator[](i); } 587 | 588 | const char &operator[](size_t i) const { return mString.operator[](i); } 589 | 590 | const char *c_str() const { return mString.c_str(); } 591 | 592 | const char *constData() const { return mString.data(); } 593 | 594 | const char *nullTerminated() const { return mString.c_str(); } 595 | 596 | size_t size() const { return mString.size(); } 597 | 598 | size_t length() const { return size(); } 599 | 600 | void truncate(size_t len) { 601 | if (mString.size() > len) 602 | mString.resize(len); 603 | } 604 | 605 | void chop(size_t s) { mString.resize(size() - s); } 606 | 607 | void resize(size_t len) { mString.resize(len); } 608 | 609 | void reserve(size_t len) { mString.reserve(len); } 610 | 611 | void prepend(const String &other) { mString.insert(0, other); } 612 | 613 | void prepend(char ch) { mString.insert(0, &ch, 1); } 614 | 615 | void insert(size_t pos, const String &text) { 616 | mString.insert(pos, text.constData(), text.size()); 617 | } 618 | 619 | void insert(size_t pos, const char *str, size_t len = npos) { 620 | if (str) { 621 | if (len == npos) 622 | len = strlen(str); 623 | mString.insert(pos, str, len); 624 | } 625 | } 626 | 627 | void insert(size_t pos, char ch) { mString.insert(pos, &ch, 1); } 628 | 629 | void append(char ch) { mString += ch; } 630 | 631 | void append(const String &ba) { mString.append(ba); } 632 | 633 | String compress() const; 634 | String uncompress() const { return uncompress(constData(), size()); } 635 | static String uncompress(const char *data, size_t size); 636 | 637 | void append(const char *str, size_t len = npos) { 638 | if (len == npos) 639 | len = strlen(str); 640 | if (len > 0) 641 | mString.append(str, len); 642 | } 643 | 644 | size_t remove(const String &str, CaseSensitivity cs = CaseSensitive) { 645 | size_t idx = 0; 646 | size_t ret = 0; 647 | while (true) { 648 | idx = indexOf(str, idx, cs); 649 | if (idx == npos) 650 | break; 651 | ++ret; 652 | remove(idx, str.size()); 653 | } 654 | return ret; 655 | } 656 | size_t remove(char ch, CaseSensitivity cs = CaseSensitive) { 657 | size_t idx = 0; 658 | size_t ret = 0; 659 | while (true) { 660 | idx = indexOf(ch, idx, cs); 661 | if (idx == npos) 662 | break; 663 | ++ret; 664 | remove(idx, 1); 665 | } 666 | return ret; 667 | } 668 | 669 | iterator erase(const_iterator begin, const_iterator end) { 670 | #ifdef HAVE_STRING_ITERATOR_ERASE 671 | return mString.erase(begin, end); 672 | #else 673 | if (begin >= end) { 674 | return mString.end(); 675 | } 676 | 677 | const size_t offset = begin - mString.begin(); 678 | mString.erase(offset, end - begin); 679 | return mString.begin() + offset; 680 | #endif 681 | } 682 | 683 | String &erase(size_t index = 0, size_t count = npos) { 684 | mString.erase(index, count); 685 | return *this; 686 | } 687 | 688 | void remove(size_t idx, size_t count) { mString.erase(idx, count); } 689 | 690 | String &operator+=(char ch) { 691 | mString += ch; 692 | return *this; 693 | } 694 | 695 | String &operator+=(const char *cstr) { 696 | if (cstr) 697 | mString += cstr; 698 | return *this; 699 | } 700 | 701 | String &operator+=(const String &other) { 702 | mString += other.mString; 703 | return *this; 704 | } 705 | 706 | size_t compare(const String &other, 707 | CaseSensitivity cs = CaseSensitive) const { 708 | if (cs == CaseSensitive) 709 | return mString.compare(other.mString); 710 | return strcasecmp(mString.c_str(), other.mString.c_str()); 711 | } 712 | 713 | bool operator==(const String &other) const { 714 | return mString == other.mString; 715 | } 716 | 717 | bool operator==(const char *other) const { 718 | return other && !mString.compare(other); 719 | } 720 | 721 | bool operator!=(const String &other) const { 722 | return mString != other.mString; 723 | } 724 | 725 | bool operator!=(const char *other) const { 726 | return !other || mString.compare(other); 727 | } 728 | 729 | bool operator<(const String &other) const { return mString < other.mString; } 730 | 731 | bool operator>(const String &other) const { return mString > other.mString; } 732 | 733 | bool endsWith(char ch, CaseSensitivity c = CaseSensitive) const { 734 | const size_t s = mString.size(); 735 | if (s) { 736 | return (c == CaseInsensitive ? tolower(at(s - 1)) == tolower(ch) 737 | : at(s - 1) == ch); 738 | } 739 | return false; 740 | } 741 | 742 | bool startsWith(char ch, CaseSensitivity c = CaseSensitive) const { 743 | if (!isEmpty()) { 744 | return (c == CaseInsensitive ? tolower(at(0)) == tolower(ch) 745 | : at(0) == ch); 746 | } 747 | return false; 748 | } 749 | 750 | bool endsWith(const String &str, CaseSensitivity cs = CaseSensitive) const { 751 | return endsWith(str.constData(), str.size(), cs); 752 | } 753 | 754 | bool endsWith(const char *str, size_t len = npos, 755 | CaseSensitivity cs = CaseSensitive) const { 756 | if (len == npos) 757 | len = strlen(str); 758 | const size_t s = mString.size(); 759 | if (s >= len) { 760 | return (cs == CaseInsensitive 761 | ? !strncasecmp(str, constData() + s - len, len) 762 | : !strncmp(str, constData() + s - len, len)); 763 | } 764 | return false; 765 | } 766 | 767 | bool startsWith(const String &str, CaseSensitivity cs = CaseSensitive) const { 768 | return startsWith(str.constData(), str.size(), cs); 769 | } 770 | 771 | bool startsWith(const char *str, size_t len = npos, 772 | CaseSensitivity cs = CaseSensitive) const { 773 | const size_t s = mString.size(); 774 | if (len == npos) 775 | len = strlen(str); 776 | if (s >= len) { 777 | return (cs == CaseInsensitive ? !strncasecmp(str, constData(), len) 778 | : !strncmp(str, constData(), len)); 779 | } 780 | return false; 781 | } 782 | 783 | void replace(size_t idx, size_t len, const String &with) { 784 | mString.replace(idx, len, with.mString); 785 | } 786 | 787 | size_t replace(const String &from, const String &to, 788 | CaseSensitivity cs = CaseSensitive) { 789 | size_t idx = 0; 790 | size_t ret = 0; 791 | while (true) { 792 | idx = indexOf(from, idx, cs); 793 | if (idx == npos) 794 | break; 795 | ++ret; 796 | replace(idx, from.size(), to); 797 | idx += to.size(); 798 | } 799 | return ret; 800 | } 801 | 802 | size_t replace(char from, char to, CaseSensitivity cs = CaseSensitive) { 803 | size_t count = 0; 804 | int i = static_cast(size() - 1); 805 | if (cs == CaseSensitive) { 806 | while (i >= 0) { 807 | char &ch = operator[](i); 808 | if (ch == from) { 809 | ch = to; 810 | ++count; 811 | } 812 | --i; 813 | } 814 | } else { 815 | from = tolower(from); 816 | while (i >= 0) { 817 | char &ch = operator[](i); 818 | if (tolower(ch) == from) { 819 | ch = to; 820 | ++count; 821 | } 822 | --i; 823 | } 824 | } 825 | return count; 826 | } 827 | 828 | String mid(size_t from, size_t l = npos) const { 829 | if (l == npos) 830 | l = size() - from; 831 | if (from == 0 && l == size()) 832 | return *this; 833 | return mString.substr(from, l); 834 | } 835 | 836 | String left(size_t l) const { return mString.substr(0, l); } 837 | 838 | String right(size_t l) const { return mString.substr(size() - l, l); } 839 | 840 | operator std::string() const { return mString; } 841 | 842 | std::string &ref() { return mString; } 843 | 844 | const std::string &ref() const { return mString; } 845 | 846 | enum SplitFlag { NoSplitFlag = 0x0, SkipEmpty = 0x1, KeepSeparators = 0x2 }; 847 | List split(char ch, unsigned int flags = NoSplitFlag) const { 848 | List ret; 849 | if (!isEmpty()) { 850 | size_t prev = 0; 851 | const size_t add = flags & KeepSeparators ? 1 : 0; 852 | while (1) { 853 | const size_t next = indexOf(ch, prev); 854 | if (next == npos) 855 | break; 856 | if (next > prev || !(flags & SkipEmpty)) 857 | ret.append(mid(prev, next - prev + add)); 858 | prev = next + 1; 859 | } 860 | if (prev < size() || !(flags & SkipEmpty)) 861 | ret.append(mid(prev)); 862 | } 863 | return ret; 864 | } 865 | 866 | List split(const String &str, 867 | unsigned int flags = NoSplitFlag) const { 868 | List ret; 869 | size_t prev = 0; 870 | while (1) { 871 | const size_t next = indexOf(str, prev); 872 | if (next == npos) 873 | break; 874 | if (next > prev || !(flags & SkipEmpty)) 875 | ret.append(mid(prev, next - prev)); 876 | prev = next + str.size(); 877 | } 878 | if (prev < size() || !(flags & SkipEmpty)) 879 | ret.append(mid(prev)); 880 | return ret; 881 | } 882 | 883 | unsigned long long toULongLong(bool *ok = 0, size_t base = 10) const { 884 | errno = 0; 885 | char *end = 0; 886 | const unsigned long long ret = ::strtoull(constData(), &end, base); 887 | if (ok) 888 | *ok = !errno && !*end; 889 | return ret; 890 | } 891 | long long toLongLong(bool *ok = 0, size_t base = 10) const { 892 | errno = 0; 893 | char *end = 0; 894 | const long long ret = ::strtoll(constData(), &end, base); 895 | if (ok) 896 | *ok = !errno && !*end; 897 | return ret; 898 | } 899 | uint32_t toULong(bool *ok = 0, size_t base = 10) const { 900 | errno = 0; 901 | char *end = 0; 902 | const uint32_t ret = ::strtoul(constData(), &end, base); 903 | if (ok) 904 | *ok = !errno && !*end; 905 | return ret; 906 | } 907 | int32_t toLong(bool *ok = 0, size_t base = 10) const { 908 | errno = 0; 909 | char *end = 0; 910 | const int32_t ret = ::strtol(constData(), &end, base); 911 | if (ok) 912 | *ok = !errno && !*end; 913 | return ret; 914 | } 915 | 916 | enum TimeFormat { DateTime, Time, Date }; 917 | 918 | static String formatTime(time_t t, TimeFormat fmt = DateTime) { 919 | const char *format = 0; 920 | switch (fmt) { 921 | case DateTime: 922 | format = "%Y-%m-%d %H:%M:%S"; 923 | break; 924 | case Date: 925 | format = "%Y-%m-%d"; 926 | break; 927 | case Time: 928 | format = "%H:%M:%S"; 929 | break; 930 | } 931 | 932 | char buf[32]; 933 | #ifdef _WIN32 934 | tm *tm = localtime(&t); 935 | const size_t w = strftime(buf, sizeof(buf), format, tm); 936 | #else 937 | tm tm; 938 | localtime_r(&t, &tm); 939 | const size_t w = strftime(buf, sizeof(buf), format, &tm); 940 | #endif 941 | return String(buf, w); 942 | } 943 | 944 | String toHex() const { return toHex(*this); } 945 | static String toHex(const String &hex) { 946 | return toHex(hex.constData(), hex.size()); 947 | } 948 | static String toHex(const void *data, size_t len); 949 | 950 | static String number(char num, size_t base = 10) { 951 | return String::number(static_cast(num), base); 952 | } 953 | static String number(unsigned char num, size_t base = 10) { 954 | return String::number(static_cast(num), base); 955 | } 956 | static String number(short num, size_t base = 10) { 957 | return String::number(static_cast(num), base); 958 | } 959 | static String number(unsigned short num, size_t base = 10) { 960 | return String::number(static_cast(num), base); 961 | } 962 | static String number(int num, size_t base = 10) { 963 | return String::number(static_cast(num), base); 964 | } 965 | static String number(unsigned int num, size_t base = 10) { 966 | return String::number(static_cast(num), base); 967 | } 968 | static String number(long num, size_t base = 10) { 969 | return String::number(static_cast(num), base); 970 | } 971 | static String number(unsigned long num, size_t base = 10) { 972 | return String::number(static_cast(num), base); 973 | } 974 | static String number(long long num, size_t base = 10) { 975 | const char *format = 0; 976 | switch (base) { 977 | case 10: 978 | format = "%lld"; 979 | break; 980 | case 16: 981 | format = "0x%llx"; 982 | break; 983 | case 8: 984 | format = "%llo"; 985 | break; 986 | case 1: { 987 | String ret; 988 | while (num) { 989 | ret.append(num & 1 ? '1' : '0'); 990 | num >>= 1; 991 | } 992 | return ret; 993 | } 994 | default: 995 | assert(0); 996 | return String(); 997 | } 998 | char buf[32]; 999 | const size_t w = ::snprintf(buf, sizeof(buf), format, num); 1000 | return String(buf, w); 1001 | } 1002 | 1003 | static String number(unsigned long long num, size_t base = 10) { 1004 | const char *format = 0; 1005 | switch (base) { 1006 | case 10: 1007 | format = "%llu"; 1008 | break; 1009 | case 16: 1010 | format = "0x%llx"; 1011 | break; 1012 | case 8: 1013 | format = "%llo"; 1014 | break; 1015 | case 1: { 1016 | String ret; 1017 | while (num) { 1018 | ret.append(num & 1 ? '1' : '0'); 1019 | num >>= 1; 1020 | } 1021 | return ret; 1022 | } 1023 | default: 1024 | assert(0); 1025 | return String(); 1026 | } 1027 | char buf[32]; 1028 | const size_t w = ::snprintf(buf, sizeof(buf), format, num); 1029 | return String(buf, w); 1030 | } 1031 | 1032 | static String number(double num, size_t prec = 2) { 1033 | char format[32]; 1034 | 1035 | // cast to unsigned because windows doesn't have %z format specifier 1036 | snprintf(format, sizeof(format), "%%.%uf", (unsigned)prec); 1037 | char buf[32]; 1038 | const size_t w = ::snprintf(buf, sizeof(buf), format, num); 1039 | return String(buf, w); 1040 | } 1041 | 1042 | static String join(const List &list, char ch) { 1043 | return String::join(list, String(&ch, 1)); 1044 | } 1045 | 1046 | static String join(const List &list, const String &sep) { 1047 | String ret; 1048 | const size_t sepSize = sep.size(); 1049 | size_t size = std::max(0, list.size() - 1) * sepSize; 1050 | const size_t count = list.size(); 1051 | for (size_t i = 0; i < count; ++i) 1052 | size += list.at(i).size(); 1053 | ret.reserve(size); 1054 | for (size_t i = 0; i < count; ++i) { 1055 | const String &b = list.at(i); 1056 | ret.append(b); 1057 | if (sepSize && i + 1 < list.size()) 1058 | ret.append(sep); 1059 | } 1060 | return ret; 1061 | } 1062 | template 1063 | static String format(const char *format, ...) RCT_PRINTF_WARNING(1, 2); 1064 | 1065 | template 1066 | static String format(const char *format, va_list args) { 1067 | va_list copy; 1068 | va_copy(copy, args); 1069 | 1070 | char buffer[StaticBufSize]; 1071 | const size_t size = ::vsnprintf(buffer, StaticBufSize, format, args); 1072 | assert(size >= 0); 1073 | String ret; 1074 | if (size < StaticBufSize) { 1075 | ret.assign(buffer, size); 1076 | } else { 1077 | ret.resize(size); 1078 | ::vsnprintf(&ret[0], size + 1, format, copy); 1079 | } 1080 | va_end(copy); 1081 | return ret; 1082 | } 1083 | 1084 | private: 1085 | std::string mString; 1086 | }; 1087 | /* fonix caa*/ 1088 | template class String; 1089 | 1090 | // Local Variables: 1091 | // beardbolt-command: "g++ -std=c++17 -O3" 1092 | // rmsbolt-command: "g++ -std=c++17 -O3" 1093 | // beardbolt-disassemble: nil 1094 | // rmsbolt-filter-directives: t 1095 | // beardbolt-asm-format: att 1096 | // rmsbolt-asm-format: "att" 1097 | // beardbolt-preserve-directives: nil 1098 | // beardbolt-preserve-unused-labels: nil 1099 | // End: 1100 | -------------------------------------------------------------------------------- /starters/unordered-multimap-emplace.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | #include 5 | 6 | int main() 7 | { 8 | std::unordered_multimap m; 9 | 10 | // uses pair's move constructor 11 | m.emplace(std::make_pair(std::string("a"), std::string("a"))); 12 | 13 | // uses pair's converting move constructor 14 | m.emplace(std::make_pair("b", "abcd")); 15 | 16 | // uses pair's template constructor 17 | m.emplace("d", "ddd"); 18 | 19 | // uses pair's piecewise constructor 20 | m.emplace(std::piecewise_construct, 21 | std::forward_as_tuple("c"), 22 | std::forward_as_tuple(10, 'c')); 23 | 24 | 25 | for (const auto &p : m) { 26 | std::cout << p.first << " => " << p.second << '\n'; 27 | } 28 | } 29 | -------------------------------------------------------------------------------- /starters/vector-emplace-back.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | #include 5 | 6 | struct President 7 | { 8 | std::string name; 9 | std::string country; 10 | int year; 11 | 12 | President(std::string p_name, std::string p_country, int p_year) 13 | : name(std::move(p_name)), country(std::move(p_country)), year(p_year) 14 | { 15 | std::cout << "I am being constructed.\n"; 16 | } 17 | President(President&& other) 18 | : name(std::move(other.name)), country(std::move(other.country)), year(other.year) 19 | { 20 | std::cout << "I am being moved.\n"; 21 | } 22 | President& operator=(const President& other) = default; 23 | }; 24 | 25 | int main() 26 | { 27 | std::vector elections; 28 | std::cout << "emplace_back:\n"; 29 | auto& ref = elections.emplace_back("Nelson Mandela", "South Africa", 1994); 30 | assert(ref.year == 1994 && "uses a reference to the created object (C++17)"); 31 | 32 | std::vector reElections; 33 | std::cout << "\npush_back:\n"; 34 | reElections.push_back(President("Franklin Delano Roosevelt", "the USA", 1936)); 35 | 36 | std::cout << "\nContents:\n"; 37 | for (President const& president: elections) { 38 | std::cout << president.name << " was elected president of " 39 | << president.country << " in " << president.year << ".\n"; 40 | } 41 | for (President const& president: reElections) { 42 | std::cout << president.name << " was re-elected president of " 43 | << president.country << " in " << president.year << ".\n"; 44 | } 45 | } 46 | 47 | // Local Variables: 48 | // beardbolt-preserve-directives: t 49 | // beardbolt-preserve-labels: t 50 | // beardbolt-command: "g++ -O3" 51 | // End: 52 | --------------------------------------------------------------------------------