├── .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 | 
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 |
--------------------------------------------------------------------------------