Mercurial > hg > gpsynth
changeset 0:add35537fdbb tip
Initial import
line wrap: on
line diff
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/.hgignore Thu Aug 25 11:05:55 2011 +0100 @@ -0,0 +1,8 @@ +syntax:glob + +.DS_Store +build/ +*.swp +gpsynth.xcodeproj/project.xcworkspace/xcuserdata +gpsynth.xcodeproj/xcuserdata/ +
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/CMakeLists.txt Thu Aug 25 11:05:55 2011 +0100 @@ -0,0 +1,13 @@ +cmake_minimum_required(VERSION 2.8) + +PROJECT(gpsynth) + +set(CMAKE_MODULE_PATH ${CMAKE_SOURCE_DIR}/third_party/cmake_modules) + +set(EXECUTABLE_OUTPUT_PATH ${PROJECT_BINARY_DIR}/bin) + +include_directories(third_party) +include_directories(src) + +add_subdirectory(third_party/json) +add_subdirectory(src)
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/COPYING Thu Aug 25 11:05:55 2011 +0100 @@ -0,0 +1,675 @@ + GNU GENERAL PUBLIC LICENSE + Version 3, 29 June 2007 + + Copyright (C) 2007 Free Software Foundation, Inc. <http://fsf.org/> + Everyone is permitted to copy and distribute verbatim copies + of this license document, but changing it is not allowed. + + Preamble + + The GNU General Public License is a free, copyleft license for +software and other kinds of works. + + The licenses for most software and other practical works are designed +to take away your freedom to share and change the works. By contrast, +the GNU General Public License is intended to guarantee your freedom to +share and change all versions of a program--to make sure it remains free +software for all its users. We, the Free Software Foundation, use the +GNU General Public License for most of our software; it applies also to +any other work released this way by its authors. You can apply it to +your programs, too. + + When we speak of free software, we are referring to freedom, not +price. Our General Public Licenses are designed to make sure that you +have the freedom to distribute copies of free software (and charge for +them if you wish), that you receive source code or can get it if you +want it, that you can change the software or use pieces of it in new +free programs, and that you know you can do these things. + + To protect your rights, we need to prevent others from denying you +these rights or asking you to surrender the rights. Therefore, you have +certain responsibilities if you distribute copies of the software, or if +you modify it: responsibilities to respect the freedom of others. + + For example, if you distribute copies of such a program, whether +gratis or for a fee, you must pass on to the recipients the same +freedoms that you received. You must make sure that they, too, receive +or can get the source code. And you must show them these terms so they +know their rights. + + Developers that use the GNU GPL protect your rights with two steps: +(1) assert copyright on the software, and (2) offer you this License +giving you legal permission to copy, distribute and/or modify it. + + For the developers' and authors' protection, the GPL clearly explains +that there is no warranty for this free software. For both users' and +authors' sake, the GPL requires that modified versions be marked as +changed, so that their problems will not be attributed erroneously to +authors of previous versions. + + Some devices are designed to deny users access to install or run +modified versions of the software inside them, although the manufacturer +can do so. This is fundamentally incompatible with the aim of +protecting users' freedom to change the software. The systematic +pattern of such abuse occurs in the area of products for individuals to +use, which is precisely where it is most unacceptable. Therefore, we +have designed this version of the GPL to prohibit the practice for those +products. If such problems arise substantially in other domains, we +stand ready to extend this provision to those domains in future versions +of the GPL, as needed to protect the freedom of users. + + Finally, every program is threatened constantly by software patents. +States should not allow patents to restrict development and use of +software on general-purpose computers, but in those that do, we wish to +avoid the special danger that patents applied to a free program could +make it effectively proprietary. To prevent this, the GPL assures that +patents cannot be used to render the program non-free. + + The precise terms and conditions for copying, distribution and +modification follow. + + TERMS AND CONDITIONS + + 0. Definitions. + + "This License" refers to version 3 of the GNU General Public License. + + "Copyright" also means copyright-like laws that apply to other kinds of +works, such as semiconductor masks. + + "The Program" refers to any copyrightable work licensed under this +License. Each licensee is addressed as "you". "Licensees" and +"recipients" may be individuals or organizations. + + To "modify" a work means to copy from or adapt all or part of the work +in a fashion requiring copyright permission, other than the making of an +exact copy. The resulting work is called a "modified version" of the +earlier work or a work "based on" the earlier work. + + A "covered work" means either the unmodified Program or a work based +on the Program. + + To "propagate" a work means to do anything with it that, without +permission, would make you directly or secondarily liable for +infringement under applicable copyright law, except executing it on a +computer or modifying a private copy. Propagation includes copying, +distribution (with or without modification), making available to the +public, and in some countries other activities as well. + + To "convey" a work means any kind of propagation that enables other +parties to make or receive copies. Mere interaction with a user through +a computer network, with no transfer of a copy, is not conveying. + + An interactive user interface displays "Appropriate Legal Notices" +to the extent that it includes a convenient and prominently visible +feature that (1) displays an appropriate copyright notice, and (2) +tells the user that there is no warranty for the work (except to the +extent that warranties are provided), that licensees may convey the +work under this License, and how to view a copy of this License. If +the interface presents a list of user commands or options, such as a +menu, a prominent item in the list meets this criterion. + + 1. Source Code. + + The "source code" for a work means the preferred form of the work +for making modifications to it. "Object code" means any non-source +form of a work. + + A "Standard Interface" means an interface that either is an official +standard defined by a recognized standards body, or, in the case of +interfaces specified for a particular programming language, one that +is widely used among developers working in that language. + + The "System Libraries" of an executable work include anything, other +than the work as a whole, that (a) is included in the normal form of +packaging a Major Component, but which is not part of that Major +Component, and (b) serves only to enable use of the work with that +Major Component, or to implement a Standard Interface for which an +implementation is available to the public in source code form. A +"Major Component", in this context, means a major essential component +(kernel, window system, and so on) of the specific operating system +(if any) on which the executable work runs, or a compiler used to +produce the work, or an object code interpreter used to run it. + + The "Corresponding Source" for a work in object code form means all +the source code needed to generate, install, and (for an executable +work) run the object code and to modify the work, including scripts to +control those activities. However, it does not include the work's +System Libraries, or general-purpose tools or generally available free +programs which are used unmodified in performing those activities but +which are not part of the work. For example, Corresponding Source +includes interface definition files associated with source files for +the work, and the source code for shared libraries and dynamically +linked subprograms that the work is specifically designed to require, +such as by intimate data communication or control flow between those +subprograms and other parts of the work. + + The Corresponding Source need not include anything that users +can regenerate automatically from other parts of the Corresponding +Source. + + The Corresponding Source for a work in source code form is that +same work. + + 2. Basic Permissions. + + All rights granted under this License are granted for the term of +copyright on the Program, and are irrevocable provided the stated +conditions are met. This License explicitly affirms your unlimited +permission to run the unmodified Program. The output from running a +covered work is covered by this License only if the output, given its +content, constitutes a covered work. This License acknowledges your +rights of fair use or other equivalent, as provided by copyright law. + + You may make, run and propagate covered works that you do not +convey, without conditions so long as your license otherwise remains +in force. You may convey covered works to others for the sole purpose +of having them make modifications exclusively for you, or provide you +with facilities for running those works, provided that you comply with +the terms of this License in conveying all material for which you do +not control copyright. Those thus making or running the covered works +for you must do so exclusively on your behalf, under your direction +and control, on terms that prohibit them from making any copies of +your copyrighted material outside their relationship with you. + + Conveying under any other circumstances is permitted solely under +the conditions stated below. Sublicensing is not allowed; section 10 +makes it unnecessary. + + 3. Protecting Users' Legal Rights From Anti-Circumvention Law. + + No covered work shall be deemed part of an effective technological +measure under any applicable law fulfilling obligations under article +11 of the WIPO copyright treaty adopted on 20 December 1996, or +similar laws prohibiting or restricting circumvention of such +measures. + + When you convey a covered work, you waive any legal power to forbid +circumvention of technological measures to the extent such circumvention +is effected by exercising rights under this License with respect to +the covered work, and you disclaim any intention to limit operation or +modification of the work as a means of enforcing, against the work's +users, your or third parties' legal rights to forbid circumvention of +technological measures. + + 4. Conveying Verbatim Copies. + + You may convey verbatim copies of the Program's source code as you +receive it, in any medium, provided that you conspicuously and +appropriately publish on each copy an appropriate copyright notice; +keep intact all notices stating that this License and any +non-permissive terms added in accord with section 7 apply to the code; +keep intact all notices of the absence of any warranty; and give all +recipients a copy of this License along with the Program. + + You may charge any price or no price for each copy that you convey, +and you may offer support or warranty protection for a fee. + + 5. Conveying Modified Source Versions. + + You may convey a work based on the Program, or the modifications to +produce it from the Program, in the form of source code under the +terms of section 4, provided that you also meet all of these conditions: + + a) The work must carry prominent notices stating that you modified + it, and giving a relevant date. + + b) The work must carry prominent notices stating that it is + released under this License and any conditions added under section + 7. This requirement modifies the requirement in section 4 to + "keep intact all notices". + + c) You must license the entire work, as a whole, under this + License to anyone who comes into possession of a copy. This + License will therefore apply, along with any applicable section 7 + additional terms, to the whole of the work, and all its parts, + regardless of how they are packaged. This License gives no + permission to license the work in any other way, but it does not + invalidate such permission if you have separately received it. + + d) If the work has interactive user interfaces, each must display + Appropriate Legal Notices; however, if the Program has interactive + interfaces that do not display Appropriate Legal Notices, your + work need not make them do so. + + A compilation of a covered work with other separate and independent +works, which are not by their nature extensions of the covered work, +and which are not combined with it such as to form a larger program, +in or on a volume of a storage or distribution medium, is called an +"aggregate" if the compilation and its resulting copyright are not +used to limit the access or legal rights of the compilation's users +beyond what the individual works permit. Inclusion of a covered work +in an aggregate does not cause this License to apply to the other +parts of the aggregate. + + 6. Conveying Non-Source Forms. + + You may convey a covered work in object code form under the terms +of sections 4 and 5, provided that you also convey the +machine-readable Corresponding Source under the terms of this License, +in one of these ways: + + a) Convey the object code in, or embodied in, a physical product + (including a physical distribution medium), accompanied by the + Corresponding Source fixed on a durable physical medium + customarily used for software interchange. + + b) Convey the object code in, or embodied in, a physical product + (including a physical distribution medium), accompanied by a + written offer, valid for at least three years and valid for as + long as you offer spare parts or customer support for that product + model, to give anyone who possesses the object code either (1) a + copy of the Corresponding Source for all the software in the + product that is covered by this License, on a durable physical + medium customarily used for software interchange, for a price no + more than your reasonable cost of physically performing this + conveying of source, or (2) access to copy the + Corresponding Source from a network server at no charge. + + c) Convey individual copies of the object code with a copy of the + written offer to provide the Corresponding Source. This + alternative is allowed only occasionally and noncommercially, and + only if you received the object code with such an offer, in accord + with subsection 6b. + + d) Convey the object code by offering access from a designated + place (gratis or for a charge), and offer equivalent access to the + Corresponding Source in the same way through the same place at no + further charge. You need not require recipients to copy the + Corresponding Source along with the object code. If the place to + copy the object code is a network server, the Corresponding Source + may be on a different server (operated by you or a third party) + that supports equivalent copying facilities, provided you maintain + clear directions next to the object code saying where to find the + Corresponding Source. Regardless of what server hosts the + Corresponding Source, you remain obligated to ensure that it is + available for as long as needed to satisfy these requirements. + + e) Convey the object code using peer-to-peer transmission, provided + you inform other peers where the object code and Corresponding + Source of the work are being offered to the general public at no + charge under subsection 6d. + + A separable portion of the object code, whose source code is excluded +from the Corresponding Source as a System Library, need not be +included in conveying the object code work. + + A "User Product" is either (1) a "consumer product", which means any +tangible personal property which is normally used for personal, family, +or household purposes, or (2) anything designed or sold for incorporation +into a dwelling. In determining whether a product is a consumer product, +doubtful cases shall be resolved in favor of coverage. For a particular +product received by a particular user, "normally used" refers to a +typical or common use of that class of product, regardless of the status +of the particular user or of the way in which the particular user +actually uses, or expects or is expected to use, the product. A product +is a consumer product regardless of whether the product has substantial +commercial, industrial or non-consumer uses, unless such uses represent +the only significant mode of use of the product. + + "Installation Information" for a User Product means any methods, +procedures, authorization keys, or other information required to install +and execute modified versions of a covered work in that User Product from +a modified version of its Corresponding Source. The information must +suffice to ensure that the continued functioning of the modified object +code is in no case prevented or interfered with solely because +modification has been made. + + If you convey an object code work under this section in, or with, or +specifically for use in, a User Product, and the conveying occurs as +part of a transaction in which the right of possession and use of the +User Product is transferred to the recipient in perpetuity or for a +fixed term (regardless of how the transaction is characterized), the +Corresponding Source conveyed under this section must be accompanied +by the Installation Information. But this requirement does not apply +if neither you nor any third party retains the ability to install +modified object code on the User Product (for example, the work has +been installed in ROM). + + The requirement to provide Installation Information does not include a +requirement to continue to provide support service, warranty, or updates +for a work that has been modified or installed by the recipient, or for +the User Product in which it has been modified or installed. Access to a +network may be denied when the modification itself materially and +adversely affects the operation of the network or violates the rules and +protocols for communication across the network. + + Corresponding Source conveyed, and Installation Information provided, +in accord with this section must be in a format that is publicly +documented (and with an implementation available to the public in +source code form), and must require no special password or key for +unpacking, reading or copying. + + 7. Additional Terms. + + "Additional permissions" are terms that supplement the terms of this +License by making exceptions from one or more of its conditions. +Additional permissions that are applicable to the entire Program shall +be treated as though they were included in this License, to the extent +that they are valid under applicable law. If additional permissions +apply only to part of the Program, that part may be used separately +under those permissions, but the entire Program remains governed by +this License without regard to the additional permissions. + + When you convey a copy of a covered work, you may at your option +remove any additional permissions from that copy, or from any part of +it. (Additional permissions may be written to require their own +removal in certain cases when you modify the work.) You may place +additional permissions on material, added by you to a covered work, +for which you have or can give appropriate copyright permission. + + Notwithstanding any other provision of this License, for material you +add to a covered work, you may (if authorized by the copyright holders of +that material) supplement the terms of this License with terms: + + a) Disclaiming warranty or limiting liability differently from the + terms of sections 15 and 16 of this License; or + + b) Requiring preservation of specified reasonable legal notices or + author attributions in that material or in the Appropriate Legal + Notices displayed by works containing it; or + + c) Prohibiting misrepresentation of the origin of that material, or + requiring that modified versions of such material be marked in + reasonable ways as different from the original version; or + + d) Limiting the use for publicity purposes of names of licensors or + authors of the material; or + + e) Declining to grant rights under trademark law for use of some + trade names, trademarks, or service marks; or + + f) Requiring indemnification of licensors and authors of that + material by anyone who conveys the material (or modified versions of + it) with contractual assumptions of liability to the recipient, for + any liability that these contractual assumptions directly impose on + those licensors and authors. + + All other non-permissive additional terms are considered "further +restrictions" within the meaning of section 10. If the Program as you +received it, or any part of it, contains a notice stating that it is +governed by this License along with a term that is a further +restriction, you may remove that term. If a license document contains +a further restriction but permits relicensing or conveying under this +License, you may add to a covered work material governed by the terms +of that license document, provided that the further restriction does +not survive such relicensing or conveying. + + If you add terms to a covered work in accord with this section, you +must place, in the relevant source files, a statement of the +additional terms that apply to those files, or a notice indicating +where to find the applicable terms. + + Additional terms, permissive or non-permissive, may be stated in the +form of a separately written license, or stated as exceptions; +the above requirements apply either way. + + 8. Termination. + + You may not propagate or modify a covered work except as expressly +provided under this License. Any attempt otherwise to propagate or +modify it is void, and will automatically terminate your rights under +this License (including any patent licenses granted under the third +paragraph of section 11). + + However, if you cease all violation of this License, then your +license from a particular copyright holder is reinstated (a) +provisionally, unless and until the copyright holder explicitly and +finally terminates your license, and (b) permanently, if the copyright +holder fails to notify you of the violation by some reasonable means +prior to 60 days after the cessation. + + Moreover, your license from a particular copyright holder is +reinstated permanently if the copyright holder notifies you of the +violation by some reasonable means, this is the first time you have +received notice of violation of this License (for any work) from that +copyright holder, and you cure the violation prior to 30 days after +your receipt of the notice. + + Termination of your rights under this section does not terminate the +licenses of parties who have received copies or rights from you under +this License. If your rights have been terminated and not permanently +reinstated, you do not qualify to receive new licenses for the same +material under section 10. + + 9. Acceptance Not Required for Having Copies. + + You are not required to accept this License in order to receive or +run a copy of the Program. Ancillary propagation of a covered work +occurring solely as a consequence of using peer-to-peer transmission +to receive a copy likewise does not require acceptance. However, +nothing other than this License grants you permission to propagate or +modify any covered work. These actions infringe copyright if you do +not accept this License. Therefore, by modifying or propagating a +covered work, you indicate your acceptance of this License to do so. + + 10. Automatic Licensing of Downstream Recipients. + + Each time you convey a covered work, the recipient automatically +receives a license from the original licensors, to run, modify and +propagate that work, subject to this License. You are not responsible +for enforcing compliance by third parties with this License. + + An "entity transaction" is a transaction transferring control of an +organization, or substantially all assets of one, or subdividing an +organization, or merging organizations. If propagation of a covered +work results from an entity transaction, each party to that +transaction who receives a copy of the work also receives whatever +licenses to the work the party's predecessor in interest had or could +give under the previous paragraph, plus a right to possession of the +Corresponding Source of the work from the predecessor in interest, if +the predecessor has it or can get it with reasonable efforts. + + You may not impose any further restrictions on the exercise of the +rights granted or affirmed under this License. For example, you may +not impose a license fee, royalty, or other charge for exercise of +rights granted under this License, and you may not initiate litigation +(including a cross-claim or counterclaim in a lawsuit) alleging that +any patent claim is infringed by making, using, selling, offering for +sale, or importing the Program or any portion of it. + + 11. Patents. + + A "contributor" is a copyright holder who authorizes use under this +License of the Program or a work on which the Program is based. The +work thus licensed is called the contributor's "contributor version". + + A contributor's "essential patent claims" are all patent claims +owned or controlled by the contributor, whether already acquired or +hereafter acquired, that would be infringed by some manner, permitted +by this License, of making, using, or selling its contributor version, +but do not include claims that would be infringed only as a +consequence of further modification of the contributor version. For +purposes of this definition, "control" includes the right to grant +patent sublicenses in a manner consistent with the requirements of +this License. + + Each contributor grants you a non-exclusive, worldwide, royalty-free +patent license under the contributor's essential patent claims, to +make, use, sell, offer for sale, import and otherwise run, modify and +propagate the contents of its contributor version. + + In the following three paragraphs, a "patent license" is any express +agreement or commitment, however denominated, not to enforce a patent +(such as an express permission to practice a patent or covenant not to +sue for patent infringement). To "grant" such a patent license to a +party means to make such an agreement or commitment not to enforce a +patent against the party. + + If you convey a covered work, knowingly relying on a patent license, +and the Corresponding Source of the work is not available for anyone +to copy, free of charge and under the terms of this License, through a +publicly available network server or other readily accessible means, +then you must either (1) cause the Corresponding Source to be so +available, or (2) arrange to deprive yourself of the benefit of the +patent license for this particular work, or (3) arrange, in a manner +consistent with the requirements of this License, to extend the patent +license to downstream recipients. "Knowingly relying" means you have +actual knowledge that, but for the patent license, your conveying the +covered work in a country, or your recipient's use of the covered work +in a country, would infringe one or more identifiable patents in that +country that you have reason to believe are valid. + + If, pursuant to or in connection with a single transaction or +arrangement, you convey, or propagate by procuring conveyance of, a +covered work, and grant a patent license to some of the parties +receiving the covered work authorizing them to use, propagate, modify +or convey a specific copy of the covered work, then the patent license +you grant is automatically extended to all recipients of the covered +work and works based on it. + + A patent license is "discriminatory" if it does not include within +the scope of its coverage, prohibits the exercise of, or is +conditioned on the non-exercise of one or more of the rights that are +specifically granted under this License. You may not convey a covered +work if you are a party to an arrangement with a third party that is +in the business of distributing software, under which you make payment +to the third party based on the extent of your activity of conveying +the work, and under which the third party grants, to any of the +parties who would receive the covered work from you, a discriminatory +patent license (a) in connection with copies of the covered work +conveyed by you (or copies made from those copies), or (b) primarily +for and in connection with specific products or compilations that +contain the covered work, unless you entered into that arrangement, +or that patent license was granted, prior to 28 March 2007. + + Nothing in this License shall be construed as excluding or limiting +any implied license or other defenses to infringement that may +otherwise be available to you under applicable patent law. + + 12. No Surrender of Others' Freedom. + + If conditions are imposed on you (whether by court order, agreement or +otherwise) that contradict the conditions of this License, they do not +excuse you from the conditions of this License. If you cannot convey a +covered work so as to satisfy simultaneously your obligations under this +License and any other pertinent obligations, then as a consequence you may +not convey it at all. For example, if you agree to terms that obligate you +to collect a royalty for further conveying from those to whom you convey +the Program, the only way you could satisfy both those terms and this +License would be to refrain entirely from conveying the Program. + + 13. Use with the GNU Affero General Public License. + + Notwithstanding any other provision of this License, you have +permission to link or combine any covered work with a work licensed +under version 3 of the GNU Affero General Public License into a single +combined work, and to convey the resulting work. The terms of this +License will continue to apply to the part which is the covered work, +but the special requirements of the GNU Affero General Public License, +section 13, concerning interaction through a network will apply to the +combination as such. + + 14. Revised Versions of this License. + + The Free Software Foundation may publish revised and/or new versions of +the GNU General Public License from time to time. Such new versions will +be similar in spirit to the present version, but may differ in detail to +address new problems or concerns. + + Each version is given a distinguishing version number. If the +Program specifies that a certain numbered version of the GNU General +Public License "or any later version" applies to it, you have the +option of following the terms and conditions either of that numbered +version or of any later version published by the Free Software +Foundation. If the Program does not specify a version number of the +GNU General Public License, you may choose any version ever published +by the Free Software Foundation. + + If the Program specifies that a proxy can decide which future +versions of the GNU General Public License can be used, that proxy's +public statement of acceptance of a version permanently authorizes you +to choose that version for the Program. + + Later license versions may give you additional or different +permissions. However, no additional obligations are imposed on any +author or copyright holder as a result of your choosing to follow a +later version. + + 15. Disclaimer of Warranty. + + THERE IS NO WARRANTY FOR THE PROGRAM, TO THE EXTENT PERMITTED BY +APPLICABLE LAW. EXCEPT WHEN OTHERWISE STATED IN WRITING THE COPYRIGHT +HOLDERS AND/OR OTHER PARTIES PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY +OF ANY KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, +THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR +PURPOSE. THE ENTIRE RISK AS TO THE QUALITY AND PERFORMANCE OF THE PROGRAM +IS WITH YOU. SHOULD THE PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF +ALL NECESSARY SERVICING, REPAIR OR CORRECTION. + + 16. Limitation of Liability. + + IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING +WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MODIFIES AND/OR CONVEYS +THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES, INCLUDING ANY +GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING OUT OF THE +USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED TO LOSS OF +DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY YOU OR THIRD +PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER PROGRAMS), +EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE POSSIBILITY OF +SUCH DAMAGES. + + 17. Interpretation of Sections 15 and 16. + + If the disclaimer of warranty and limitation of liability provided +above cannot be given local legal effect according to their terms, +reviewing courts shall apply local law that most closely approximates +an absolute waiver of all civil liability in connection with the +Program, unless a warranty or assumption of liability accompanies a +copy of the Program in return for a fee. + + END OF TERMS AND CONDITIONS + + How to Apply These Terms to Your New Programs + + If you develop a new program, and you want it to be of the greatest +possible use to the public, the best way to achieve this is to make it +free software which everyone can redistribute and change under these terms. + + To do so, attach the following notices to the program. It is safest +to attach them to the start of each source file to most effectively +state the exclusion of warranty; and each file should have at least +the "copyright" line and a pointer to where the full notice is found. + + <one line to give the program's name and a brief idea of what it does.> + Copyright (C) <year> <name of author> + + This program is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see <http://www.gnu.org/licenses/>. + +Also add information on how to contact you by electronic and paper mail. + + If the program does terminal interaction, make it output a short +notice like this when it starts in an interactive mode: + + <program> Copyright (C) <year> <name of author> + This program comes with ABSOLUTELY NO WARRANTY; for details type `show w'. + This is free software, and you are welcome to redistribute it + under certain conditions; type `show c' for details. + +The hypothetical commands `show w' and `show c' should show the appropriate +parts of the General Public License. Of course, your program's commands +might be different; for a GUI interface, you would use an "about box". + + You should also get your employer (if you work as a programmer) or school, +if any, to sign a "copyright disclaimer" for the program, if necessary. +For more information on this, and how to apply and follow the GNU GPL, see +<http://www.gnu.org/licenses/>. + + The GNU General Public License does not permit incorporating your program +into proprietary programs. If your program is a subroutine library, you +may consider it more useful to permit linking proprietary applications with +the library. If this is what you want to do, use the GNU Lesser General +Public License instead of this License. But first, please read +<http://www.gnu.org/philosophy/why-not-lgpl.html>. +
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/README Thu Aug 25 11:05:55 2011 +0100 @@ -0,0 +1,75 @@ +-------------------------------------------------------------------------------- +GPSynth (c) 2011 Ian Hobson - ian.r.hobson@gmail.com +-------------------------------------------------------------------------------- + +GPSynth is a program that uses genetic programming techniques to artificially +evolve SuperCollider synthesizers towards the ability to emulate a target sound +file. + +GPSynth is open source software, released under a GPL license. +See the COPYING file for information on how this software can be used. + + +-------------------------------------------------------------------------------- +Requirements +-------------------------------------------------------------------------------- + +GPSynth has been tested on OS X 10.6.8. It should compile on Linux but has not +been tested yet. Support for Linux will be added soon. + +GPSynth requires SuperCollider to be installed on your system. If you don't have +SuperCollider in /Applications/SuperCollider then you need to specify the path +to its location with the --scpath option. + + +-------------------------------------------------------------------------------- +Usage +-------------------------------------------------------------------------------- + +In its most simple form, gpsynth takes a single argument, --target: + +gpsynth --target test.wav + +GPSynth uses Libsndfile to load audio file data, for a list of supported formats +see http://www.mega-nerd.com/libsndfile. + +A timestamped folder will be created in the current path, use the --workfolder +option to change where the folder will be created. + +See gpsynth --help for a full list of options. + + +-------------------------------------------------------------------------------- +Build instructions +-------------------------------------------------------------------------------- + +Building GPSynth has been tested on Mac OS X 10.6.8 with LLVM 3.0 and GCC 4.2. + +You need the following libraries installed on your system to build GPSynth. +Boost (at least v1.44, tested against v1.47) with libraries compiled with +multi-threading enabled. +LibSndFile +FFTW + +If these libraries are not on your system, an easy way to get them is with +Homebrew (http://mxcl.github.com/homebrew/). After installing homebrew the +following commands should get your system ready to compile GPSynth: + +brew install boost +brew install libsndfile +brew install fftw + +An XCode 4 project is included with GPSynth for convenience, however the best +way to install GPSynth is by using the CMake build scripts. If you don't have +CMake on your system then it can be installed with Homebrew: +brew install cmake + +cd in to the root of the gpsynth source directory (where this README file is), +then perform the following commands: +mkdir build +cd build +cmake .. +sudo make install + + +
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/gpsynth.xcodeproj/project.pbxproj Thu Aug 25 11:05:55 2011 +0100 @@ -0,0 +1,411 @@ +// !$*UTF8*$! +{ + archiveVersion = 1; + classes = { + }; + objectVersion = 46; + objects = { + +/* Begin PBXBuildFile section */ + 3C646D2513F4720B0058A207 /* feature_extractor.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 3C646D0B13F4720B0058A207 /* feature_extractor.cpp */; }; + 3C646D2613F4720B0058A207 /* file_comparer.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 3C646D0D13F4720B0058A207 /* file_comparer.cpp */; }; + 3C646D2713F4720B0058A207 /* logger.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 3C646D1113F4720B0058A207 /* logger.cpp */; }; + 3C646D2813F4720B0058A207 /* main.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 3C646D1313F4720B0058A207 /* main.cpp */; }; + 3C646D2913F4720B0058A207 /* mfcc_analyzer.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 3C646D1413F4720B0058A207 /* mfcc_analyzer.cpp */; }; + 3C646D2A13F4720B0058A207 /* population.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 3C646D1613F4720B0058A207 /* population.cpp */; }; + 3C646D2B13F4720B0058A207 /* sc_converter.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 3C646D1A13F4720B0058A207 /* sc_converter.cpp */; }; + 3C646D2C13F4720B0058A207 /* sc_evaluator.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 3C646D1C13F4720B0058A207 /* sc_evaluator.cpp */; }; + 3C646D2D13F4720B0058A207 /* sc_grammar.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 3C646D1E13F4720B0058A207 /* sc_grammar.cpp */; }; + 3C646DA813F701AD0058A207 /* program_options.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 3C646DA713F701AD0058A207 /* program_options.cpp */; }; + 3CC27D2E13B8247000456569 /* libboost_thread-mt.dylib in Frameworks */ = {isa = PBXBuildFile; fileRef = 3CC27D2D13B8247000456569 /* libboost_thread-mt.dylib */; }; + 3CC5E42813C0099B0000CF05 /* json_reader.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 3CC5E41F13C0099B0000CF05 /* json_reader.cpp */; }; + 3CC5E42913C0099B0000CF05 /* json_value.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 3CC5E42113C0099B0000CF05 /* json_value.cpp */; }; + 3CC5E42A13C0099B0000CF05 /* json_writer.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 3CC5E42313C0099B0000CF05 /* json_writer.cpp */; }; + 3CD21C6913D4973000E33C4F /* libboost_system-mt.dylib in Frameworks */ = {isa = PBXBuildFile; fileRef = 3CD21C6813D4973000E33C4F /* libboost_system-mt.dylib */; }; + 3CD21C6B13D4973A00E33C4F /* libboost_filesystem-mt.dylib in Frameworks */ = {isa = PBXBuildFile; fileRef = 3CD21C6A13D4973A00E33C4F /* libboost_filesystem-mt.dylib */; }; + 3CD21C6E13D4F10000E33C4F /* libboost_date_time-mt.dylib in Frameworks */ = {isa = PBXBuildFile; fileRef = 3CD21C6D13D4F10000E33C4F /* libboost_date_time-mt.dylib */; }; + 3CD21C7013D506F100E33C4F /* libsndfile.dylib in Frameworks */ = {isa = PBXBuildFile; fileRef = 3CD21C6F13D506F100E33C4F /* libsndfile.dylib */; }; + 3CD21C7213D51F9000E33C4F /* libfftw3.3.dylib in Frameworks */ = {isa = PBXBuildFile; fileRef = 3CD21C7113D51F9000E33C4F /* libfftw3.3.dylib */; }; + 3CDEC1E613F2082A0075D229 /* libboost_program_options-mt.dylib in Frameworks */ = {isa = PBXBuildFile; fileRef = 3CDEC1E513F2082A0075D229 /* libboost_program_options-mt.dylib */; }; + 3CF213E71405FEB20080579B /* sc_default_grammar.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 3CF213E61405FEB20080579B /* sc_default_grammar.cpp */; }; +/* End PBXBuildFile section */ + +/* Begin PBXCopyFilesBuildPhase section */ + 3CC27D1D13B7E6EC00456569 /* CopyFiles */ = { + isa = PBXCopyFilesBuildPhase; + buildActionMask = 2147483647; + dstPath = /usr/share/man/man1/; + dstSubfolderSpec = 0; + files = ( + ); + runOnlyForDeploymentPostprocessing = 1; + }; +/* End PBXCopyFilesBuildPhase section */ + +/* Begin PBXFileReference section */ + 3C646D0713F4720B0058A207 /* boost_ex.hpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.h; path = boost_ex.hpp; sourceTree = "<group>"; }; + 3C646D0813F4720B0058A207 /* converter.hpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.h; path = converter.hpp; sourceTree = "<group>"; }; + 3C646D0A13F4720B0058A207 /* evaluator.hpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.h; path = evaluator.hpp; sourceTree = "<group>"; }; + 3C646D0B13F4720B0058A207 /* feature_extractor.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = feature_extractor.cpp; sourceTree = "<group>"; }; + 3C646D0C13F4720B0058A207 /* feature_extractor.hpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.h; path = feature_extractor.hpp; sourceTree = "<group>"; }; + 3C646D0D13F4720B0058A207 /* file_comparer.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = file_comparer.cpp; sourceTree = "<group>"; }; + 3C646D0E13F4720B0058A207 /* file_comparer.hpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.h; path = file_comparer.hpp; sourceTree = "<group>"; }; + 3C646D0F13F4720B0058A207 /* grammar.hpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.h; path = grammar.hpp; sourceTree = "<group>"; }; + 3C646D1013F4720B0058A207 /* graph_helpers.hpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.h; path = graph_helpers.hpp; sourceTree = "<group>"; }; + 3C646D1113F4720B0058A207 /* logger.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = logger.cpp; sourceTree = "<group>"; }; + 3C646D1213F4720B0058A207 /* logger.hpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.h; path = logger.hpp; sourceTree = "<group>"; }; + 3C646D1313F4720B0058A207 /* main.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = main.cpp; sourceTree = "<group>"; }; + 3C646D1413F4720B0058A207 /* mfcc_analyzer.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = mfcc_analyzer.cpp; sourceTree = "<group>"; }; + 3C646D1513F4720B0058A207 /* mfcc_analyzer.hpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.h; path = mfcc_analyzer.hpp; sourceTree = "<group>"; }; + 3C646D1613F4720B0058A207 /* population.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = population.cpp; sourceTree = "<group>"; }; + 3C646D1713F4720B0058A207 /* population.hpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.h; path = population.hpp; sourceTree = "<group>"; }; + 3C646D1813F4720B0058A207 /* precompiled_header.hpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.h; path = precompiled_header.hpp; sourceTree = "<group>"; }; + 3C646D1913F4720B0058A207 /* range.hpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.h; path = range.hpp; sourceTree = "<group>"; }; + 3C646D1A13F4720B0058A207 /* sc_converter.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = sc_converter.cpp; sourceTree = "<group>"; }; + 3C646D1B13F4720B0058A207 /* sc_converter.hpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.h; path = sc_converter.hpp; sourceTree = "<group>"; }; + 3C646D1C13F4720B0058A207 /* sc_evaluator.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = sc_evaluator.cpp; sourceTree = "<group>"; }; + 3C646D1D13F4720B0058A207 /* sc_evaluator.hpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.h; path = sc_evaluator.hpp; sourceTree = "<group>"; }; + 3C646D1E13F4720B0058A207 /* sc_grammar.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = sc_grammar.cpp; sourceTree = "<group>"; }; + 3C646D1F13F4720B0058A207 /* sc_grammar.hpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.h; path = sc_grammar.hpp; sourceTree = "<group>"; }; + 3C646D2013F4720B0058A207 /* spectrum_analyzer.hpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.h; path = spectrum_analyzer.hpp; sourceTree = "<group>"; }; + 3C646D2113F4720B0058A207 /* statistics.hpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.h; path = statistics.hpp; sourceTree = "<group>"; }; + 3C646D2213F4720B0058A207 /* std_ex.hpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.h; path = std_ex.hpp; sourceTree = "<group>"; }; + 3C646D2313F4720B0058A207 /* synth_graph.hpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.h; path = synth_graph.hpp; sourceTree = "<group>"; }; + 3C646D2413F4720B0058A207 /* window_makers.hpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.h; path = window_makers.hpp; sourceTree = "<group>"; }; + 3C646DA713F701AD0058A207 /* program_options.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = program_options.cpp; sourceTree = "<group>"; }; + 3C646DAA13F701C60058A207 /* program_options.hpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.h; path = program_options.hpp; sourceTree = "<group>"; }; + 3CC27D1F13B7E6EC00456569 /* gpsynth */ = {isa = PBXFileReference; explicitFileType = "compiled.mach-o.executable"; includeInIndex = 0; path = gpsynth; sourceTree = BUILT_PRODUCTS_DIR; }; + 3CC27D2D13B8247000456569 /* libboost_thread-mt.dylib */ = {isa = PBXFileReference; lastKnownFileType = "compiled.mach-o.dylib"; name = "libboost_thread-mt.dylib"; path = "usr/local/lib/libboost_thread-mt.dylib"; sourceTree = SDKROOT; }; + 3CC5E41613C0099B0000CF05 /* assertions.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = assertions.h; sourceTree = "<group>"; }; + 3CC5E41713C0099B0000CF05 /* autolink.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = autolink.h; sourceTree = "<group>"; }; + 3CC5E41813C0099B0000CF05 /* config.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = config.h; sourceTree = "<group>"; }; + 3CC5E41913C0099B0000CF05 /* features.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = features.h; sourceTree = "<group>"; }; + 3CC5E41A13C0099B0000CF05 /* forwards.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = forwards.h; sourceTree = "<group>"; }; + 3CC5E41B13C0099B0000CF05 /* json.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = json.h; sourceTree = "<group>"; }; + 3CC5E41C13C0099B0000CF05 /* json_batchallocator.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = json_batchallocator.h; sourceTree = "<group>"; }; + 3CC5E41D13C0099B0000CF05 /* json_internalarray.inl */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text; path = json_internalarray.inl; sourceTree = "<group>"; }; + 3CC5E41E13C0099B0000CF05 /* json_internalmap.inl */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text; path = json_internalmap.inl; sourceTree = "<group>"; }; + 3CC5E41F13C0099B0000CF05 /* json_reader.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = json_reader.cpp; sourceTree = "<group>"; }; + 3CC5E42013C0099B0000CF05 /* json_tool.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = json_tool.h; sourceTree = "<group>"; }; + 3CC5E42113C0099B0000CF05 /* json_value.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = json_value.cpp; sourceTree = "<group>"; }; + 3CC5E42213C0099B0000CF05 /* json_valueiterator.inl */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text; path = json_valueiterator.inl; sourceTree = "<group>"; }; + 3CC5E42313C0099B0000CF05 /* json_writer.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = json_writer.cpp; sourceTree = "<group>"; }; + 3CC5E42413C0099B0000CF05 /* LICENSE */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text; path = LICENSE; sourceTree = "<group>"; }; + 3CC5E42513C0099B0000CF05 /* reader.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = reader.h; sourceTree = "<group>"; }; + 3CC5E42613C0099B0000CF05 /* value.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = value.h; sourceTree = "<group>"; }; + 3CC5E42713C0099B0000CF05 /* writer.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = writer.h; sourceTree = "<group>"; }; + 3CD21C6813D4973000E33C4F /* libboost_system-mt.dylib */ = {isa = PBXFileReference; lastKnownFileType = "compiled.mach-o.dylib"; name = "libboost_system-mt.dylib"; path = "usr/local/lib/libboost_system-mt.dylib"; sourceTree = SDKROOT; }; + 3CD21C6A13D4973A00E33C4F /* libboost_filesystem-mt.dylib */ = {isa = PBXFileReference; lastKnownFileType = "compiled.mach-o.dylib"; name = "libboost_filesystem-mt.dylib"; path = "usr/local/lib/libboost_filesystem-mt.dylib"; sourceTree = SDKROOT; }; + 3CD21C6D13D4F10000E33C4F /* libboost_date_time-mt.dylib */ = {isa = PBXFileReference; lastKnownFileType = "compiled.mach-o.dylib"; name = "libboost_date_time-mt.dylib"; path = "usr/local/lib/libboost_date_time-mt.dylib"; sourceTree = SDKROOT; }; + 3CD21C6F13D506F100E33C4F /* libsndfile.dylib */ = {isa = PBXFileReference; lastKnownFileType = "compiled.mach-o.dylib"; name = libsndfile.dylib; path = usr/local/lib/libsndfile.dylib; sourceTree = SDKROOT; }; + 3CD21C7113D51F9000E33C4F /* libfftw3.3.dylib */ = {isa = PBXFileReference; lastKnownFileType = "compiled.mach-o.dylib"; name = libfftw3.3.dylib; path = ../../../../usr/local/Cellar/fftw/3.2.2/lib/libfftw3.3.dylib; sourceTree = "<group>"; }; + 3CDEC1E513F2082A0075D229 /* libboost_program_options-mt.dylib */ = {isa = PBXFileReference; lastKnownFileType = "compiled.mach-o.dylib"; name = "libboost_program_options-mt.dylib"; path = "usr/local/lib/libboost_program_options-mt.dylib"; sourceTree = SDKROOT; }; + 3CF213E41405FE460080579B /* sc_default_grammar.hpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.h; path = sc_default_grammar.hpp; sourceTree = "<group>"; }; + 3CF213E61405FEB20080579B /* sc_default_grammar.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = sc_default_grammar.cpp; sourceTree = "<group>"; }; + 3CF213E8140618AA0080579B /* CMakeLists.txt */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text; path = CMakeLists.txt; sourceTree = "<group>"; }; +/* End PBXFileReference section */ + +/* Begin PBXFrameworksBuildPhase section */ + 3CC27D1C13B7E6EC00456569 /* Frameworks */ = { + isa = PBXFrameworksBuildPhase; + buildActionMask = 2147483647; + files = ( + 3CD21C7213D51F9000E33C4F /* libfftw3.3.dylib in Frameworks */, + 3CD21C7013D506F100E33C4F /* libsndfile.dylib in Frameworks */, + 3CD21C6E13D4F10000E33C4F /* libboost_date_time-mt.dylib in Frameworks */, + 3CD21C6B13D4973A00E33C4F /* libboost_filesystem-mt.dylib in Frameworks */, + 3CDEC1E613F2082A0075D229 /* libboost_program_options-mt.dylib in Frameworks */, + 3CD21C6913D4973000E33C4F /* libboost_system-mt.dylib in Frameworks */, + 3CC27D2E13B8247000456569 /* libboost_thread-mt.dylib in Frameworks */, + ); + runOnlyForDeploymentPostprocessing = 0; + }; +/* End PBXFrameworksBuildPhase section */ + +/* Begin PBXGroup section */ + 3C646D0613F4720B0058A207 /* src */ = { + isa = PBXGroup; + children = ( + 3CF213E8140618AA0080579B /* CMakeLists.txt */, + 3C646D0713F4720B0058A207 /* boost_ex.hpp */, + 3C646D0813F4720B0058A207 /* converter.hpp */, + 3C646D0A13F4720B0058A207 /* evaluator.hpp */, + 3C646D0B13F4720B0058A207 /* feature_extractor.cpp */, + 3C646D0C13F4720B0058A207 /* feature_extractor.hpp */, + 3C646D0D13F4720B0058A207 /* file_comparer.cpp */, + 3C646D0E13F4720B0058A207 /* file_comparer.hpp */, + 3C646D0F13F4720B0058A207 /* grammar.hpp */, + 3C646D1013F4720B0058A207 /* graph_helpers.hpp */, + 3C646D1113F4720B0058A207 /* logger.cpp */, + 3C646D1213F4720B0058A207 /* logger.hpp */, + 3C646D1313F4720B0058A207 /* main.cpp */, + 3C646D1413F4720B0058A207 /* mfcc_analyzer.cpp */, + 3C646D1513F4720B0058A207 /* mfcc_analyzer.hpp */, + 3C646D1613F4720B0058A207 /* population.cpp */, + 3C646D1713F4720B0058A207 /* population.hpp */, + 3C646DA713F701AD0058A207 /* program_options.cpp */, + 3C646DAA13F701C60058A207 /* program_options.hpp */, + 3C646D1813F4720B0058A207 /* precompiled_header.hpp */, + 3C646D1913F4720B0058A207 /* range.hpp */, + 3C646D1A13F4720B0058A207 /* sc_converter.cpp */, + 3C646D1B13F4720B0058A207 /* sc_converter.hpp */, + 3CF213E41405FE460080579B /* sc_default_grammar.hpp */, + 3CF213E61405FEB20080579B /* sc_default_grammar.cpp */, + 3C646D1C13F4720B0058A207 /* sc_evaluator.cpp */, + 3C646D1D13F4720B0058A207 /* sc_evaluator.hpp */, + 3C646D1E13F4720B0058A207 /* sc_grammar.cpp */, + 3C646D1F13F4720B0058A207 /* sc_grammar.hpp */, + 3C646D2013F4720B0058A207 /* spectrum_analyzer.hpp */, + 3C646D2113F4720B0058A207 /* statistics.hpp */, + 3C646D2213F4720B0058A207 /* std_ex.hpp */, + 3C646D2313F4720B0058A207 /* synth_graph.hpp */, + 3C646D2413F4720B0058A207 /* window_makers.hpp */, + ); + path = src; + sourceTree = "<group>"; + }; + 3CC27D1413B7E6EC00456569 = { + isa = PBXGroup; + children = ( + 3C646D0613F4720B0058A207 /* src */, + 3CC5E41513C0099B0000CF05 /* json */, + 3CD21C7113D51F9000E33C4F /* libfftw3.3.dylib */, + 3CD21C6F13D506F100E33C4F /* libsndfile.dylib */, + 3CD21C6D13D4F10000E33C4F /* libboost_date_time-mt.dylib */, + 3CD21C6A13D4973A00E33C4F /* libboost_filesystem-mt.dylib */, + 3CDEC1E513F2082A0075D229 /* libboost_program_options-mt.dylib */, + 3CD21C6813D4973000E33C4F /* libboost_system-mt.dylib */, + 3CC27D2D13B8247000456569 /* libboost_thread-mt.dylib */, + 3CC27D2013B7E6EC00456569 /* Products */, + ); + sourceTree = "<group>"; + }; + 3CC27D2013B7E6EC00456569 /* Products */ = { + isa = PBXGroup; + children = ( + 3CC27D1F13B7E6EC00456569 /* gpsynth */, + ); + name = Products; + sourceTree = "<group>"; + }; + 3CC5E41513C0099B0000CF05 /* json */ = { + isa = PBXGroup; + children = ( + 3CC5E41613C0099B0000CF05 /* assertions.h */, + 3CC5E41713C0099B0000CF05 /* autolink.h */, + 3CC5E41813C0099B0000CF05 /* config.h */, + 3CC5E41913C0099B0000CF05 /* features.h */, + 3CC5E41A13C0099B0000CF05 /* forwards.h */, + 3CC5E41B13C0099B0000CF05 /* json.h */, + 3CC5E41C13C0099B0000CF05 /* json_batchallocator.h */, + 3CC5E41D13C0099B0000CF05 /* json_internalarray.inl */, + 3CC5E41E13C0099B0000CF05 /* json_internalmap.inl */, + 3CC5E41F13C0099B0000CF05 /* json_reader.cpp */, + 3CC5E42013C0099B0000CF05 /* json_tool.h */, + 3CC5E42113C0099B0000CF05 /* json_value.cpp */, + 3CC5E42213C0099B0000CF05 /* json_valueiterator.inl */, + 3CC5E42313C0099B0000CF05 /* json_writer.cpp */, + 3CC5E42413C0099B0000CF05 /* LICENSE */, + 3CC5E42513C0099B0000CF05 /* reader.h */, + 3CC5E42613C0099B0000CF05 /* value.h */, + 3CC5E42713C0099B0000CF05 /* writer.h */, + ); + name = json; + path = third_party/json; + sourceTree = "<group>"; + }; +/* End PBXGroup section */ + +/* Begin PBXNativeTarget section */ + 3CC27D1E13B7E6EC00456569 /* gpsynth */ = { + isa = PBXNativeTarget; + buildConfigurationList = 3CC27D2813B7E6EC00456569 /* Build configuration list for PBXNativeTarget "gpsynth" */; + buildPhases = ( + 3CC27D1B13B7E6EC00456569 /* Sources */, + 3CC27D1C13B7E6EC00456569 /* Frameworks */, + 3CC27D1D13B7E6EC00456569 /* CopyFiles */, + ); + buildRules = ( + ); + dependencies = ( + ); + name = gpsynth; + productName = gpsynth; + productReference = 3CC27D1F13B7E6EC00456569 /* gpsynth */; + productType = "com.apple.product-type.tool"; + }; +/* End PBXNativeTarget section */ + +/* Begin PBXProject section */ + 3CC27D1613B7E6EC00456569 /* Project object */ = { + isa = PBXProject; + attributes = { + LastUpgradeCheck = 0420; + }; + buildConfigurationList = 3CC27D1913B7E6EC00456569 /* Build configuration list for PBXProject "gpsynth" */; + compatibilityVersion = "Xcode 3.2"; + developmentRegion = English; + hasScannedForEncodings = 0; + knownRegions = ( + en, + ); + mainGroup = 3CC27D1413B7E6EC00456569; + productRefGroup = 3CC27D2013B7E6EC00456569 /* Products */; + projectDirPath = ""; + projectRoot = ""; + targets = ( + 3CC27D1E13B7E6EC00456569 /* gpsynth */, + ); + }; +/* End PBXProject section */ + +/* Begin PBXSourcesBuildPhase section */ + 3CC27D1B13B7E6EC00456569 /* Sources */ = { + isa = PBXSourcesBuildPhase; + buildActionMask = 2147483647; + files = ( + 3CC5E42813C0099B0000CF05 /* json_reader.cpp in Sources */, + 3CC5E42913C0099B0000CF05 /* json_value.cpp in Sources */, + 3CC5E42A13C0099B0000CF05 /* json_writer.cpp in Sources */, + 3C646D2513F4720B0058A207 /* feature_extractor.cpp in Sources */, + 3C646D2613F4720B0058A207 /* file_comparer.cpp in Sources */, + 3C646D2713F4720B0058A207 /* logger.cpp in Sources */, + 3C646D2813F4720B0058A207 /* main.cpp in Sources */, + 3C646D2913F4720B0058A207 /* mfcc_analyzer.cpp in Sources */, + 3C646D2A13F4720B0058A207 /* population.cpp in Sources */, + 3C646D2B13F4720B0058A207 /* sc_converter.cpp in Sources */, + 3C646D2C13F4720B0058A207 /* sc_evaluator.cpp in Sources */, + 3C646D2D13F4720B0058A207 /* sc_grammar.cpp in Sources */, + 3C646DA813F701AD0058A207 /* program_options.cpp in Sources */, + 3CF213E71405FEB20080579B /* sc_default_grammar.cpp in Sources */, + ); + runOnlyForDeploymentPostprocessing = 0; + }; +/* End PBXSourcesBuildPhase section */ + +/* Begin XCBuildConfiguration section */ + 3CC27D2613B7E6EC00456569 /* Debug */ = { + isa = XCBuildConfiguration; + buildSettings = { + ARCHS = "$(NATIVE_ARCH_ACTUAL)"; + DEAD_CODE_STRIPPING = NO; + GCC_C_LANGUAGE_STANDARD = gnu99; + GCC_OPTIMIZATION_LEVEL = 0; + GCC_PRECOMPILE_PREFIX_HEADER = YES; + GCC_PREFIX_HEADER = src/precompiled_header.hpp; + GCC_PREPROCESSOR_DEFINITIONS = DEBUG; + GCC_SYMBOLS_PRIVATE_EXTERN = NO; + GCC_VERSION = com.apple.compilers.llvm.clang.1_0; + GCC_WARN_64_TO_32_BIT_CONVERSION = YES; + GCC_WARN_ABOUT_RETURN_TYPE = YES; + GCC_WARN_UNUSED_VARIABLE = YES; + HEADER_SEARCH_PATHS = ( + /usr/local/include, + "$(PROJECT_DIR)/../common", + "$(PROJECT_DIR)/third_party", + /usr/local/include/libjson, + "$(PROJECT_DIR/src", + ); + MACOSX_DEPLOYMENT_TARGET = 10.6; + ONLY_ACTIVE_ARCH = YES; + SDKROOT = macosx; + }; + name = Debug; + }; + 3CC27D2713B7E6EC00456569 /* Release */ = { + isa = XCBuildConfiguration; + buildSettings = { + ARCHS = "$(NATIVE_ARCH_ACTUAL)"; + DEAD_CODE_STRIPPING = YES; + GCC_C_LANGUAGE_STANDARD = gnu99; + GCC_PRECOMPILE_PREFIX_HEADER = YES; + GCC_PREFIX_HEADER = src/precompiled_header.hpp; + GCC_VERSION = com.apple.compilers.llvm.clang.1_0; + GCC_WARN_64_TO_32_BIT_CONVERSION = YES; + GCC_WARN_ABOUT_RETURN_TYPE = YES; + GCC_WARN_UNUSED_VARIABLE = YES; + HEADER_SEARCH_PATHS = ( + /usr/local/include, + "$(PROJECT_DIR)/../common", + "$(PROJECT_DIR)/third_party", + /usr/local/include/libjson, + "$(PROJECT_DIR/src", + ); + MACOSX_DEPLOYMENT_TARGET = 10.6; + SDKROOT = macosx; + }; + name = Release; + }; + 3CC27D2913B7E6EC00456569 /* Debug */ = { + isa = XCBuildConfiguration; + buildSettings = { + ALWAYS_SEARCH_USER_PATHS = NO; + COPY_PHASE_STRIP = NO; + GCC_DYNAMIC_NO_PIC = NO; + GCC_ENABLE_OBJC_EXCEPTIONS = YES; + GCC_PREFIX_HEADER = "$(PROJECT_DIR)/src/precompiled_header.hpp"; + GCC_VERSION = com.apple.compilers.llvm.clang.1_0; + HEADER_SEARCH_PATHS = ( + /usr/local/include, + "$(PROJECT_DIR)/third_party", + /usr/local/include/libjson, + "$(PROJECT_DIR/include", + ); + LIBRARY_SEARCH_PATHS = ( + "$(inherited)", + /usr/local/Cellar/fftw/3.2.2/lib, + /usr/local/Cellar/libxtract/0.6.3/lib, + ); + PRODUCT_NAME = "$(TARGET_NAME)"; + }; + name = Debug; + }; + 3CC27D2A13B7E6EC00456569 /* Release */ = { + isa = XCBuildConfiguration; + buildSettings = { + ALWAYS_SEARCH_USER_PATHS = NO; + COPY_PHASE_STRIP = YES; + DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym"; + GCC_ENABLE_OBJC_EXCEPTIONS = YES; + GCC_PREFIX_HEADER = "$(PROJECT_DIR)/src/precompiled_header.hpp"; + GCC_VERSION = com.apple.compilers.llvm.clang.1_0; + HEADER_SEARCH_PATHS = ( + /usr/local/include, + "$(PROJECT_DIR)/third_party", + /usr/local/include/libjson, + "$(PROJECT_DIR/include", + ); + LIBRARY_SEARCH_PATHS = ( + "$(inherited)", + /usr/local/Cellar/fftw/3.2.2/lib, + /usr/local/Cellar/libxtract/0.6.3/lib, + ); + PRODUCT_NAME = "$(TARGET_NAME)"; + }; + name = Release; + }; +/* End XCBuildConfiguration section */ + +/* Begin XCConfigurationList section */ + 3CC27D1913B7E6EC00456569 /* Build configuration list for PBXProject "gpsynth" */ = { + isa = XCConfigurationList; + buildConfigurations = ( + 3CC27D2613B7E6EC00456569 /* Debug */, + 3CC27D2713B7E6EC00456569 /* Release */, + ); + defaultConfigurationIsVisible = 0; + defaultConfigurationName = Release; + }; + 3CC27D2813B7E6EC00456569 /* Build configuration list for PBXNativeTarget "gpsynth" */ = { + isa = XCConfigurationList; + buildConfigurations = ( + 3CC27D2913B7E6EC00456569 /* Debug */, + 3CC27D2A13B7E6EC00456569 /* Release */, + ); + defaultConfigurationIsVisible = 0; + defaultConfigurationName = Release; + }; +/* End XCConfigurationList section */ + }; + rootObject = 3CC27D1613B7E6EC00456569 /* Project object */; +}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/gpsynth.xcodeproj/project.xcworkspace/contents.xcworkspacedata Thu Aug 25 11:05:55 2011 +0100 @@ -0,0 +1,7 @@ +<?xml version="1.0" encoding="UTF-8"?> +<Workspace + version = "1.0"> + <FileRef + location = "self:gpsynth.xcodeproj"> + </FileRef> +</Workspace>
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/gpsynth.xcodeproj/xcshareddata/xcschemes/gpsynth.xcscheme Thu Aug 25 11:05:55 2011 +0100 @@ -0,0 +1,209 @@ +<?xml version="1.0" encoding="UTF-8"?> +<Scheme + version = "1.8"> + <BuildAction + parallelizeBuildables = "YES" + buildImplicitDependencies = "YES"> + <BuildActionEntries> + <BuildActionEntry + buildForTesting = "YES" + buildForRunning = "YES" + buildForProfiling = "YES" + buildForArchiving = "YES" + buildForAnalyzing = "YES"> + <BuildableReference + BuildableIdentifier = "primary" + BlueprintIdentifier = "3CC27D1E13B7E6EC00456569" + BuildableName = "gpsynth" + BlueprintName = "gpsynth" + ReferencedContainer = "container:gpsynth.xcodeproj"> + </BuildableReference> + </BuildActionEntry> + </BuildActionEntries> + </BuildAction> + <TestAction + selectedDebuggerIdentifier = "Xcode.DebuggerFoundation.Debugger.GDB" + selectedLauncherIdentifier = "Xcode.DebuggerFoundation.Launcher.GDB" + shouldUseLaunchSchemeArgsEnv = "YES" + buildConfiguration = "Debug"> + <Testables> + </Testables> + </TestAction> + <LaunchAction + selectedDebuggerIdentifier = "Xcode.DebuggerFoundation.Debugger.GDB" + selectedLauncherIdentifier = "Xcode.DebuggerFoundation.Launcher.GDB" + displayScaleIsEnabled = "NO" + displayScale = "1.00" + launchStyle = "0" + useCustomWorkingDirectory = "NO" + buildConfiguration = "Release" + debugDocumentVersioning = "YES" + allowLocationSimulation = "YES"> + <BuildableProductRunnable> + <BuildableReference + BuildableIdentifier = "primary" + BlueprintIdentifier = "3CC27D1E13B7E6EC00456569" + BuildableName = "gpsynth" + BlueprintName = "gpsynth" + ReferencedContainer = "container:gpsynth.xcodeproj"> + </BuildableReference> + </BuildableProductRunnable> + <CommandLineArguments> + <CommandLineArgument + argument = "--help" + isEnabled = "NO"> + </CommandLineArgument> + <CommandLineArgument + argument = "--maxtreedepth 3" + isEnabled = "YES"> + </CommandLineArgument> + <CommandLineArgument + argument = "--corelimit 1" + isEnabled = "NO"> + </CommandLineArgument> + <CommandLineArgument + argument = "--windowsize 1024" + isEnabled = "YES"> + </CommandLineArgument> + <CommandLineArgument + argument = "--hopsize 256" + isEnabled = "YES"> + </CommandLineArgument> + <CommandLineArgument + argument = "--keepfolders 1" + isEnabled = "NO"> + </CommandLineArgument> + <CommandLineArgument + argument = "--reproducebest 1" + isEnabled = "YES"> + </CommandLineArgument> + <CommandLineArgument + argument = "--crossover 0.5 --mutation 0.45" + isEnabled = "YES"> + </CommandLineArgument> + <CommandLineArgument + argument = "--tournament 2" + isEnabled = "YES"> + </CommandLineArgument> + <CommandLineArgument + argument = "--population 10000" + isEnabled = "NO"> + </CommandLineArgument> + <CommandLineArgument + argument = "--population 1000" + isEnabled = "NO"> + </CommandLineArgument> + <CommandLineArgument + argument = "--population 100" + isEnabled = "YES"> + </CommandLineArgument> + <CommandLineArgument + argument = "--generations 20" + isEnabled = "NO"> + </CommandLineArgument> + <CommandLineArgument + argument = "--generations 1000" + isEnabled = "YES"> + </CommandLineArgument> + <CommandLineArgument + argument = "--workfolder "/tmp"" + isEnabled = "YES"> + </CommandLineArgument> + <CommandLineArgument + argument = "--grammar "/users/ian/dropbox/uni/Msc Project/sc.json"" + isEnabled = "NO"> + </CommandLineArgument> + <CommandLineArgument + argument = "--target "/users/ian/samples/test/808_snare.wav"" + isEnabled = "NO"> + </CommandLineArgument> + <CommandLineArgument + argument = "--target "/users/ian/samples/test/808_kick_long.wav"" + isEnabled = "NO"> + </CommandLineArgument> + <CommandLineArgument + argument = "--target "/users/ian/samples/test/lfo.wav"" + isEnabled = "NO"> + </CommandLineArgument> + <CommandLineArgument + argument = "--target "/users/ian/samples/test/horn.wav"" + isEnabled = "NO"> + </CommandLineArgument> + <CommandLineArgument + argument = "--target "/users/ian/samples/test/white_noise_lpf_up_32.aif"" + isEnabled = "NO"> + </CommandLineArgument> + <CommandLineArgument + argument = "--target "/users/ian/samples/test/fm_bell.wav"" + isEnabled = "NO"> + </CommandLineArgument> + <CommandLineArgument + argument = "--target "/users/ian/samples/test/rect_110.wav"" + isEnabled = "NO"> + </CommandLineArgument> + <CommandLineArgument + argument = "--target "/users/ian/samples/test/lawnmower.wav"" + isEnabled = "NO"> + </CommandLineArgument> + <CommandLineArgument + argument = "--target "/users/ian/samples/test/piano.wav"" + isEnabled = "NO"> + </CommandLineArgument> + <CommandLineArgument + argument = "--target "/users/ian/samples/test/bloom.wav"" + isEnabled = "NO"> + </CommandLineArgument> + <CommandLineArgument + argument = "--target "/users/ian/samples/test/speak_and_spell_a.wav"" + isEnabled = "NO"> + </CommandLineArgument> + <CommandLineArgument + argument = "--target "/users/ian/samples/test/speak_and_spell_house.wav"" + isEnabled = "NO"> + </CommandLineArgument> + <CommandLineArgument + argument = "--target "/users/ian/samples/test/speak_and_spell_happy.wav"" + isEnabled = "YES"> + </CommandLineArgument> + <CommandLineArgument + argument = "--target "/users/ian/samples/test/ghosts.wav"" + isEnabled = "NO"> + </CommandLineArgument> + <CommandLineArgument + argument = "--target "/users/ian/samples/test/omnichord_58.wav"" + isEnabled = "NO"> + </CommandLineArgument> + <CommandLineArgument + argument = "--target "/users/ian/samples/test/vec2_synth_063.wav"" + isEnabled = "NO"> + </CommandLineArgument> + </CommandLineArguments> + <AdditionalOptions> + </AdditionalOptions> + </LaunchAction> + <ProfileAction + displayScaleIsEnabled = "NO" + displayScale = "1.00" + shouldUseLaunchSchemeArgsEnv = "YES" + savedToolIdentifier = "" + useCustomWorkingDirectory = "NO" + buildConfiguration = "Release" + debugDocumentVersioning = "YES"> + <BuildableProductRunnable> + <BuildableReference + BuildableIdentifier = "primary" + BlueprintIdentifier = "3CC27D1E13B7E6EC00456569" + BuildableName = "gpsynth" + BlueprintName = "gpsynth" + ReferencedContainer = "container:gpsynth.xcodeproj"> + </BuildableReference> + </BuildableProductRunnable> + </ProfileAction> + <AnalyzeAction + buildConfiguration = "Debug"> + </AnalyzeAction> + <ArchiveAction + buildConfiguration = "Release" + revealArchiveInOrganizer = "YES"> + </ArchiveAction> +</Scheme>
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/grammar/sc.json Thu Aug 25 11:05:55 2011 +0100 @@ -0,0 +1,298 @@ +{ + "sclang-version": "3", + "name": "sc3", + "arg-types": [ + { + "type": "frequency", + "range": [20, 20000], + "range_control": [0, 100], + "scaling": "log" + }, + { + "type": "radians", + "range": [0, 6.2831853072] + }, + { + "type": "audio", + "range": [-1, 1] + }, + { + "type": "time", + "range": [0, 10], + "scaling": "log" + }, + { + "type": "trigger", + "range": [-1, 1], + "fixed_range": true + } + ], + + "sources": [ + // oscillators + { + "name": "SinOsc", + "args": [ + { "name": "freq", "type": "frequency" }, + { "name": "phase", "type": "radians" } + ] + }, + { + "name": "Saw", + "args": { "name": "freq", "type": "frequency" } + }, + { + "name": "SyncSaw", + "args": [ + { "name": "syncFreq", "type": "frequency" }, + { "name": "sawFreq", "type": "frequency" } + ] + }, + { + "name": "VarSaw", + "args": [ { "name": "freq", "type": "frequency" }, "iphase", "width" ] + }, + { + "name": "Pulse", + "args": [ { "name": "freq", "type": "frequency" }, "width" ] + }, + { + "name": "Impulse", + "args": [ { "name": "freq", "type": "frequency" }, "phase" ] + }, + { + "name": "Blip", + "args": [ + { "name": "freq", "type": "frequency" }, + { "name": "numharm", "range": [1, 200] } + ] + }, + { + "name": "SinOscFB", + "args": [ + { "name": "freq", "type": "frequency" }, + { "name": "feedback", "type": "radians" } + ] + }, + { + "name": "PMOsc", + "args": [ + { "name": "carfreq", "type": "frequency" }, + { "name": "modfreq", "type": "frequency" }, + { "name": "pmindex", "type": "radians" }, + { "name": "modphase", "type": "radians" } + ] + }, + { + "name": "FBSineC", + "output": "audio", + "args": [ + { "name": "freq", "type": "frequency" }, + { "name": "im", "range": [1, 64] }, + { "name": "fb", "range": [0, 0.5] }, + { "name": "a", "range": [1, 1.2] }, + { "name": "c", "range": [0.1, 0.9] } + ] + }, + { + "name": "FBSineN", + "output": "audio", + "args": [ + { "name": "freq", "type": "frequency" }, + { "name": "im", "range": [1, 64] }, + { "name": "fb", "range": [0, 0.5] }, + { "name": "a", "range": [1, 1.2] }, + { "name": "c", "range": [0.1, 0.9] } + ] + }, + { + "name": "Logistic", + "args": [ + { "name": "chaosParam", "range": [3, 3.999] }, + { "name": "freq", "type": "frequency" } + ] + }, + // Noise + { + "name": "LFNoise0", + "args": { "name": "freq", "type": "frequency" } + }, + "WhiteNoise", + "BrownNoise", + "PinkNoise", + { + "name": "Crackle", + "args": { "name": "chaosParam", "range": [1, 2] } + }, + // Lines/Envelopes + { + "name": "Line", + "output": "control", + "args": { "name": "dur", "type": "time" } + }, + { + "name": "XLine", + "output": "control", + "args": [ + { "name": "dur", "type": "time" }, + { "name": "add", "constant": -1 } + ] + }, + { + "name": "Linen", + "output": "control", + "args": [ + "gate", + { "name": "attackTime", "type": "time" }, + "susLevel", + { "name": "releaseTime", "type": "time" } + ] + } + ], + + "modifiers": [ + // Filters + { + "name": "LPF", + "args": { "name": "freq", "type": "frequency" } + }, + { + "name": "HPF", + "args": { "name": "freq", "type": "frequency" } + }, + { + "name": "BPF", + "args": [ { "name": "freq", "type": "frequency" }, "rq" ] + }, + { + "name": "BRF", + "args": [ { "name": "freq", "type": "frequency" }, "rq" ] + }, + { + "name": "RLPF", + "args": [ { "name": "freq", "type": "frequency" }, "rq" ] + }, + { + "name": "RHPF", + "args": [ { "name": "freq", "type": "frequency" }, "rq" ] + }, + { + "name": "Resonz", + "args": [ { "name": "freq", "type": "frequency" }, "bwr" ] + }, + { + "name": "Ringz", + "args": [ + { "name": "freq", "type": "frequency" }, + { "name": "decaytime", "type": "time" } + ] + }, + { + "name": "Formlet", + "args": [ + { "name": "freq", "type": "frequency" }, + { "name": "attacktime", "type": "time" }, + { "name": "decaytime", "type": "time" } + ] + }, + { + "name": "MoogFF", + "args": [ + { "name": "freq", "type": "frequency" }, + { "name": "gain", "range": [0, 4] } + ] + }, + { + "name": "FreeVerb", + "output": "audio", + "args": [ "mix", "room", "damp" ] + }, + // Dynamics + { + "name": "Limiter", + "output": "audio", + "args": "level" + }, + { + "name": "Amplitude", + "output": "control", + "args": [ + { "name": "attackTime", "type": "time" }, + { "name": "releaseTime", "type": "time" } + ] + }, + { + "name": "Normalizer", + "output": "audio", + "args": [ "level", { "name": "dur", "range": [0.001, 0.5] } ] + }, + { + "name": "Compander", + "output": "audio", + "args": [ + { "name": "control", "type": "audio" }, + "thresh", + { "name": "slopeBelow", "type": "time" }, + { "name": "slopeAbove", "type": "time" }, + { "name": "clampTime", "range": [0.001, 0.2] }, + { "name": "relaxTime", "range": [0.001, 0.2] } + ] + }, + // shifters + { + "name": "FreqShift", + "output": "audio", + "args": [ + { "name": "freq", "range": [-1000, 1000] }, + { "name": "phase", "type": "radians" } + ] + }, + { + "name": "PitchShift", + "output": "audio", + "args": [ + { "name": "pitchRatio", "range": [0, 4] }, + { "name": "pitchDispersion", "range": [0, 4] }, + { "name": "timeDispersion", "range": [0, 0.2] } + ] + }, + // delays + { + "name": "DelayC", + "args": [ + { "name": "maxdelaytime", "constant": 1 }, + { "name": "delaytime", "range": [0.001, 1] } + ] + }, + { + "name": "Pluck", + "output": "audio", + "args": [ + { "name": "trig", "type": "trigger" }, + { "name": "maxdelaytime", "constant": 1 }, + { "name": "delaytime" }, + { "name": "decaytime", "type": "time" }, + { "name": "coef", "range": [-0.9, 0.9] } + ] + }, + { + "name": "CombC", + "args": [ + { "name": "maxdelaytime", "constant": 1 }, + { "name": "delaytime" }, + { "name": "decaytime", "range": [-1, 1] } + ] + }, + // misc + { + "name": "Decay", + "args": { "name": "decayTime", "type": "time" } + }, + { + "name": "Latch", + "args": { "name": "trig", "type": "trigger" } + } + ] // end of modifiers +} + +
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/src/CMakeLists.txt Thu Aug 25 11:05:55 2011 +0100 @@ -0,0 +1,44 @@ +set(GPSYNTH_SOURCES + feature_extractor.cpp + file_comparer.cpp + logger.cpp + main.cpp + mfcc_analyzer.cpp + population.cpp + program_options.cpp + sc_converter.cpp + sc_default_grammar.cpp + sc_evaluator.cpp + sc_grammar.cpp +) + +# json +include_directories("${PROJECT_DIR}/third_party/json") +set(GPSYNTH_LIBS ${GPSYNTH_LIBS} json) + +# fftw +find_package(FFTW REQUIRED) +include_directories(${FFTW_INCLUDES}) +set(GPSYNTH_LIBS ${GPSYNTH_LIBS} ${FFTW_LIBRARIES}) + +# sndfile +find_package(SndFile REQUIRED) +include_directories(${SNDFILE_INCLUDE_DIR}) +set(GPSYNTH_LIBS ${GPSYNTH_LIBS} ${SNDFILE_LIBRARY}) + +# configure boost +set(Boost_USE_MULTITHREADED ON) +find_package(Boost 1.44.0 COMPONENTS date_time + filesystem + program_options + system + thread + REQUIRED) +set(GPSYNTH_LIBS ${GPSYNTH_LIBS} ${Boost_LIBRARIES}) + +# setup gpsynth +add_executable(gpsynth ${GPSYNTH_SOURCES}) +target_link_libraries(gpsynth ${GPSYNTH_LIBS}) + +install(TARGETS gpsynth RUNTIME DESTINATION bin) +
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/src/boost_ex.hpp Thu Aug 25 11:05:55 2011 +0100 @@ -0,0 +1,73 @@ +// Copyright 2011, Ian Hobson. +// +// This file is part of gpsynth. +// +// gpsynth is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// gpsynth is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with gpsynth in the file COPYING. +// If not, see http://www.gnu.org/licenses/. + +// Boost helper functions + +#pragma once + +#include "boost/foreach.hpp" +#include "boost/random/mersenne_twister.hpp" +#include "boost/random/normal_distribution.hpp" +#include "boost/random/variate_generator.hpp" +#include "boost/shared_array.hpp" +#include "boost/thread.hpp" + +#include <cstddef> +#include <ctime> + +// foreach macro +#define foreach BOOST_FOREACH +#define foreach_reverse BOOST_REVERSE_FOREACH + +namespace bx { + + // Sleep the current thread for a certain number of milliseconds + inline void SleepMs(int milliseconds) { + boost::this_thread::sleep(boost::posix_time::milliseconds(milliseconds)); + } + + // boost.random helpers + typedef boost::random::mt19937 EngineType; + typedef boost::variate_generator<EngineType, boost::normal_distribution<> > + GaussianGenerator; + + class Random { + EngineType engine_; + + public: + Random() : engine_(static_cast<unsigned int>(std::time(0))) {} + EngineType& Engine() { return engine_; } + + static Random& Singleton() { + static Random random; + return random; + } + }; + + static GaussianGenerator MakeGaussianGenerator(double mean = 0.0, + double sigma = 1.0) { + boost::normal_distribution<> distribution(mean, sigma); + return GaussianGenerator(Random::Singleton().Engine(), distribution); + } + + static EngineType& RandomEngine() { + return Random::Singleton().Engine(); + } + + +} // bx namespace
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/src/converter.hpp Thu Aug 25 11:05:55 2011 +0100 @@ -0,0 +1,32 @@ +// Copyright 2011, Ian Hobson. +// +// This file is part of gpsynth. +// +// gpsynth is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// gpsynth is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with gpsynth in the file COPYING. +// If not, see http://www.gnu.org/licenses/. + +// Graph Converter Interface + +#pragma once + +#include "synth_graph.hpp" + +#include <string> + +struct ConverterInterface { + virtual std::string ToDOT(const sg::Graph& graph) = 0; + virtual void Export(const sg::Graph& graph, + const std::string& export_folder, + const std::string& export_name) = 0; +};
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/src/evaluator.hpp Thu Aug 25 11:05:55 2011 +0100 @@ -0,0 +1,38 @@ +// Copyright 2011, Ian Hobson. +// +// This file is part of gpsynth. +// +// gpsynth is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// gpsynth is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with gpsynth in the file COPYING. +// If not, see http://www.gnu.org/licenses/. + +// Graph evaluator interface + +#pragma once + +#include "synth_graph.hpp" + +#include <cstddef> +#include <string> +#include <vector> + +// Used to listen to evaluator work thread notifications +struct EvaluatorListenerInterface { + virtual void GraphRatedNotification(std::size_t graphs_rated) = 0; +}; + +struct EvaluatorInterface { + virtual void RateGraphs(std::vector<sg::Graph>& graph) = 0; + virtual void SetWorkFolder(const std::string& path) = 0; + virtual void SetListener(EvaluatorListenerInterface* listener) = 0; +};
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/src/feature_extractor.cpp Thu Aug 25 11:05:55 2011 +0100 @@ -0,0 +1,412 @@ +// Copyright 2011, Ian Hobson. +// +// This file is part of gpsynth. +// +// gpsynth is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// gpsynth is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with gpsynth in the file COPYING. +// If not, see http://www.gnu.org/licenses/. + +#include "feature_extractor.hpp" + +#include "statistics.hpp" +#include "std_ex.hpp" +#include "window_makers.hpp" + +#include "sndfile.hh" + +#include "boost/bind.hpp" +#include "boost/filesystem.hpp" + +#include <algorithm> +#include <cmath> +#include <iostream> +#include <stdexcept> +#include <sstream> + +namespace bf = boost::filesystem; + +namespace dsp { + +FeatureExtractor::FeatureExtractor(const std::string& file_path /* = "" */, + int window_size /* = 2048 */, + int hop_size /* = 512 */) +: window_size_(-1), // initialized by SetFrameSize + hop_size_(hop_size), + bin_frequencies_(window_size), + spectrum_analyzer_(window_size), + mfcc_analyzer_(window_size, 40, 13, 30, 10000) +{ + SetWindowSize(window_size); + if (!file_path.empty()) { + LoadFile(file_path); + } +} + +void FeatureExtractor::ClearCaches() { + spectrum_.clear(); + magnitude_spectrum_.clear(); + log_magnitude_spectrum_.clear(); + spectral_centroid_.clear(); + spectral_spread_.clear(); + spectral_flux_.clear(); + pitch_.clear(); + energy_.clear(); + mfccs_.clear(); + delta_mfccs_.clear(); + double_delta_mfccs_.clear(); +} + +void FeatureExtractor::SetWindowSize(int window_size) { + if (window_size_ != window_size) { + window_size_ = window_size; + frame_.resize(window_size); + // generate new window + window_.resize(window_size); + dsp::HammingWindow(window_size, window_.begin()); + ClearCaches(); + } +} + +void FeatureExtractor::SetHopSize(int hop_size) { + if (hop_size_ != hop_size) { + hop_size_ = hop_size; + ClearCaches(); + } +} + +void FeatureExtractor::LoadFile(const std::string& file_path) { + SndfileHandle file(file_path); + if (file.error()) { + std::stringstream message; + message << "FeatureExtractor::ExtractFeaturesFromFile: " + << "Error reading from file '" << file_path << "' " + << '(' << file.error() << ')'; + throw std::runtime_error(message.str()); + } + sf_count_t frames = file.frames(); + int channels = file.channels(); + file_data_.resize(frames * channels); + // read the audio data in to the buffer + file.readf(&file_data_[0], frames); + // only supporting mono at the moment, remove extra channels + if (channels > 1) { + for (int i = 0; i < frames; i++) { + file_data_[i] = file_data_[i * channels]; + } + file_data_.resize(frames); + } + // success, clear caches + ClearCaches(); + // update file info + file_path_ = file_path; + sample_rate_ = file.samplerate(); + frames_ = static_cast<int>(file_data_.size()) / hop_size_; + if (frames_ == 0) { + frames_++; + } + // bin info + bin_size_ = sample_rate_ / static_cast<Value>(window_size_); + std::generate(bin_frequencies_.begin(), bin_frequencies_.end(), + stdx::Stepper<Value>(bin_size_)); +} + +Value FeatureExtractor::Duration() const { + return file_data_.size() / static_cast<Value>(sample_rate_); +} + +const std::vector<ComplexValueList>& FeatureExtractor::Spectrum() { + if (spectrum_.empty()) { + const int frame_size = window_size_; + const int half_frame_size = frame_size / 2; + const int hop_size = hop_size_; + spectrum_.resize(frames_); + ValueList::const_iterator data_begin = file_data_.begin(); + ValueList::const_iterator data_end = file_data_.end(); + for (int i = 0; i < frames_; i++) { + //std::cout << "frame:" << i << '\n'; + // find the frame bounds + ValueList::const_iterator read_start = data_begin + (i * hop_size); + ValueList::const_iterator read_end = read_start + frame_size; + if (read_end > data_end) { + read_end = data_end; + } + // extract the frame and apply window + std::transform(read_start, read_end, window_.begin(), + frame_.begin(), std::multiplies<Value>()); + // on the last frame we'll need to fill the rest of the frame with zeros + std::size_t data_read = read_end - read_start; + if (data_read < frame_size) { + std::fill(frame_.begin() + data_read, frame_.end(), 0); + } + // get the spectrum of the current frame + std::copy(frame_.begin(), frame_.end(), spectrum_analyzer_.Input()); + spectrum_analyzer_.Execute(); + spectrum_[i].assign(spectrum_analyzer_.Output(), + spectrum_analyzer_.Output() + half_frame_size); + } + } + return spectrum_; +} + +const std::vector<ValueList>& FeatureExtractor::MagnitudeSpectrum() { + if (magnitude_spectrum_.empty()) { + // get complex spectrum + const std::vector<ComplexValueList>& spectrum = Spectrum(); + // get magnitude spectrum for each frame + std::size_t frames = spectrum.size(); + magnitude_spectrum_.resize(frames); + for (int i = 0; i < frames; i++) { + // take the absolute value of each complex bin + magnitude_spectrum_[i].resize(spectrum[i].size()); + std::transform(spectrum[i].begin(), spectrum[i].end(), + magnitude_spectrum_[i].begin(), std::abs<Value>); + } + } + return magnitude_spectrum_; +} + +const std::vector<ValueList>& FeatureExtractor::LogMagnitudeSpectrum() { + if (log_magnitude_spectrum_.empty()) { + // get magnitude spectrum + const std::vector<ValueList>& magnitude_spectrum = MagnitudeSpectrum(); + // get magnitude spectrum for each frame + std::size_t frames = magnitude_spectrum.size(); + log_magnitude_spectrum_.resize(frames); + for (int i = 0; i < frames; i++) { + log_magnitude_spectrum_[i].resize(magnitude_spectrum[i].size()); + // copy magnitudes to log magnitude buffer, avoiding zeros + std::replace_copy(magnitude_spectrum[i].begin(), + magnitude_spectrum[i].end(), + log_magnitude_spectrum_[i].begin(), + 0.0, + std::numeric_limits<Value>::min()); + // take the log of each magnitude (ugly casting for cmath function...) + std::transform(log_magnitude_spectrum_[i].begin(), + log_magnitude_spectrum_[i].end(), + log_magnitude_spectrum_[i].begin(), + static_cast<double(*)(double)>(std::log10)); + } + } + return log_magnitude_spectrum_; +} + +const ValueList& FeatureExtractor::SpectralCentroid() { + if (spectral_centroid_.empty()) { + MagnitudeSpectrum(); + std::size_t frames = magnitude_spectrum_.size(); + spectral_centroid_.resize(frames); + std::size_t bins = magnitude_spectrum_[0].size(); + ValueList weighted_frequencies(bins); + for (int i = 0; i < frames; i++) { + const ValueList& magnitudes = magnitude_spectrum_[i]; + // get bin frequencies weighted by bin magnitudes + std::transform(magnitudes.begin(), + magnitudes.end(), + bin_frequencies_.begin(), + weighted_frequencies.begin(), + std::multiplies<Value>()); + // divide mean of weighted frequencies by mean of bin magnitudes + Value magnitude = stats::Sum(magnitudes); + if (magnitude > 0) { + spectral_centroid_[i] = stats::Sum(weighted_frequencies) / magnitude; + } else { + spectral_centroid_[i] = 0; + } + } + } + return spectral_centroid_; +} + + +const ValueList& FeatureExtractor::SpectralSpread() { + if (spectral_spread_.empty()) { + SpectralCentroid(); + std::size_t frames = magnitude_spectrum_.size(); + spectral_spread_.resize(frames); + std::size_t bins = magnitude_spectrum_[0].size(); + ValueList weighted_difference(bins); + for (int i = 0; i < frames; i++) { + Value centroid = spectral_centroid_[i]; + const ValueList& magnitudes = magnitude_spectrum_[i]; + for (int bin = 0; bin < bins; bin++) { + Value difference = bin_frequencies_[bin] - centroid; + weighted_difference[bin] = difference * difference * magnitudes[bin]; + } + Value magnitude = stats::Sum(magnitudes); + if (magnitude > 0) { + Value variance = stats::Sum(weighted_difference) / magnitude; + spectral_spread_[i] = std::sqrt(variance); + } else { + spectral_spread_[i] = 0; + } + } + } + return spectral_spread_; +} + +const ValueList& FeatureExtractor::Pitch() { + if (pitch_.empty()) { + MagnitudeSpectrum(); + pitch_.resize(frames_); + std::deque<Value> recent_pitches; + const int moving_average_size = 8; + for (int i = 0; i < frames_; i++) { + const ValueList& mags = magnitude_spectrum_[i]; + // find the peak bin number + std::ptrdiff_t peak_bin = std::distance(mags.begin(), + std::max_element(mags.begin(), + mags.end())); + // interpolate peak frequency + double offset; + double centre = mags[peak_bin]; + if (centre == 0) { + pitch_[i] = 0; + } else { + if (peak_bin == 0) { + // parabolic interpolation of first 2 bin magnitudes + double above = mags[peak_bin + 1]; + offset = above / (2 * (2 * centre - above)); + } else if (peak_bin == mags.size() - 1) { + // parabolic interpolation of last 2 bin magnitudes + double below = mags[peak_bin - 1]; + offset = (-below) / (2 * (2 * centre - below)); + } else { + double below = mags[peak_bin - 1]; + double above = mags[peak_bin + 1]; + // gaussian interpolation + offset = std::log(above / below) + / (2 * std::log((centre * centre) / (above * below))); + } + Value frame_pitch = bin_size_ * (peak_bin + offset); + if (recent_pitches.size() == moving_average_size) { + recent_pitches.pop_front(); + } + recent_pitches.push_back(frame_pitch); + pitch_[i] = stats::Mean(recent_pitches); + } + } + } + return pitch_; +} + +const ValueList& FeatureExtractor::Energy() { + if (energy_.empty()) { + const int frame_size = window_size_; + const int hop_size = hop_size_; + energy_.resize(frames_); + ValueList::const_iterator data_begin = file_data_.begin(); + ValueList::const_iterator data_end = file_data_.end(); + for (int i = 0; i < frames_; i++) { + // find the frame bounds + ValueList::const_iterator read_start = data_begin + (i * hop_size); + ValueList::const_iterator read_end = read_start + frame_size; + if (read_end > data_end) { + read_end = data_end; + } + Value energy(0); + std::ptrdiff_t samples = std::distance(read_start, read_end); + while (read_start != read_end) { + energy += std::pow(*read_start++, 2); + } + energy_[i] = energy / samples; + } + } + return energy_; +} + +const std::vector<ValueList>& FeatureExtractor::MFCCs() { + if (mfccs_.empty()) { + const std::vector<ValueList>& magnitude_spectrum = MagnitudeSpectrum(); + mfcc_analyzer_.SetSampleRate(sample_rate_); + mfccs_.resize(frames_); + for (int i = 0; i < frames_; i++) { + mfccs_[i] = mfcc_analyzer_.ExtractMFCCS(magnitude_spectrum[i]); + } + } + return mfccs_; +} + +const std::vector<ValueList>& FeatureExtractor::DeltaMFCCs() { + if (delta_mfccs_.empty()) { + const std::vector<ValueList>& mfccs = MFCCs(); + // resize buffers + const std::size_t number_of_mfccs = mfccs[0].size(); + ValueList empty_values(number_of_mfccs, 0); + delta_mfccs_.resize(mfccs.size(), empty_values); + // calculate deltas based on 2 neighbouring frames in either direction + for (int i = 2, last_frame = frames_ - 2; i < last_frame; i++) { + const ValueList& before_2 = mfccs[i - 2]; + const ValueList& before_1 = mfccs[i - 1]; + const ValueList& after_1 = mfccs[i + 1]; + const ValueList& after_2 = mfccs[i + 2]; + ValueList& delta_frame = delta_mfccs_[i]; + for (int m = 0; m < number_of_mfccs; m++) { + Value delta = after_1[m] - before_1[m] + + 2.0 * (after_2[m] - before_2[m]); + delta_frame[m] = delta / 10.0; + } + } + } + return delta_mfccs_; +} + +const std::vector<ValueList>& FeatureExtractor::DoubleDeltaMFCCs() { + if (double_delta_mfccs_.empty()) { + const std::vector<ValueList>& delta_mfccs = DeltaMFCCs(); + // resize buffers + const std::size_t number_of_mfccs = delta_mfccs[0].size(); + ValueList empty_values(number_of_mfccs, 0); + double_delta_mfccs_.resize(delta_mfccs.size(), empty_values); + // calculate deltas based on 2 neighbouring frames in either direction + for (int i = 2, last_frame = frames_ - 2; i < last_frame; i++) { + const ValueList& before_2 = delta_mfccs[i - 2]; + const ValueList& before_1 = delta_mfccs[i - 1]; + const ValueList& after_1 = delta_mfccs[i + 1]; + const ValueList& after_2 = delta_mfccs[i + 2]; + ValueList& double_delta_frame = double_delta_mfccs_[i]; + for (int m = 0; m < number_of_mfccs; m++) { + Value delta = after_1[m] - before_1[m] + + 2.0 * (after_2[m] - before_2[m]); + double_delta_frame[m] = delta / 10.0; + } + } + } + return double_delta_mfccs_; +} + +const std::vector<ValueList>& FeatureExtractor::SpectralFlux() { + if (spectral_flux_.empty()) { + // get magnitude spectrum + const std::vector<ValueList>& spectrum = MagnitudeSpectrum(); + // get magnitude spectrum for each frame + std::size_t frames = spectrum.size(); + std::size_t bins = spectrum[0].size(); + spectral_flux_.resize(frames); + // first frame always zeroed + spectral_flux_[0].assign(bins, 0); + for (int i = 1; i < frames; i++) { + const ValueList& spectrum_frame = spectrum[i]; + const ValueList& previous_spectrum_frame = spectrum[i - 1]; + ValueList& flux_frame = spectral_flux_[i]; + flux_frame.resize(bins); + // get the difference in magnitudes for each bin + for (int bin = 0; bin < bins; bin++) { + flux_frame[bin] = spectrum_frame[bin] - previous_spectrum_frame[bin]; + } + //std::cout << i << ": " << stats::Mean(flux_frame) << '\n'; + } + } + return spectral_flux_; +} + +} // dsp namespace
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/src/feature_extractor.hpp Thu Aug 25 11:05:55 2011 +0100 @@ -0,0 +1,99 @@ +// Copyright 2011, Ian Hobson. +// +// This file is part of gpsynth. +// +// gpsynth is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// gpsynth is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with gpsynth in the file COPYING. +// If not, see http://www.gnu.org/licenses/. + +// FeatureExtractor - responsible for managing the retrieval of features from +// audio data. Caches feature data to avoid recalculations when accessing +// different features that have the same dependencies. + +#pragma once + +#include "mfcc_analyzer.hpp" +#include "spectrum_analyzer.hpp" + +#include <complex> +#include <vector> + +namespace dsp { + +typedef double Value; +typedef std::vector<Value> ValueList; +typedef std::complex<Value> ComplexValue; +typedef std::vector<ComplexValue> ComplexValueList; + +class FeatureExtractor { + std::string file_path_; + ValueList window_; + ValueList frame_; + ValueList file_data_; + ValueList bin_frequencies_; + Value bin_size_; + std::vector<ComplexValueList> spectrum_; + std::vector<ValueList> magnitude_spectrum_; + std::vector<ValueList> log_magnitude_spectrum_; + std::vector<ValueList> mfccs_; + std::vector<ValueList> delta_mfccs_; + std::vector<ValueList> double_delta_mfccs_; + std::vector<ValueList> spectral_flux_; + ValueList pitch_; + ValueList spectral_centroid_; + ValueList spectral_spread_; + ValueList energy_; + + int frames_; + int window_size_; + int hop_size_; + Value sample_rate_; + + dsp::SpectrumAnalyzer spectrum_analyzer_; + dsp::MFCCAnalyzer mfcc_analyzer_; + +public: + FeatureExtractor(const std::string& file_path = "", + int frame_size = 2048, + int hop_size = 512); + + void LoadFile(const std::string& file_path); + void ClearCaches(); + + int Frames() const { return frames_; } + + int WindowSize() const { return window_size_; } + int HopSize() const { return hop_size_; } + + void SetWindowSize(int window_size); + void SetHopSize(int hop_size); + + Value Duration() const; + + const std::vector<ComplexValueList>& Spectrum(); + const std::vector<ValueList>& MagnitudeSpectrum(); + const std::vector<ValueList>& LogMagnitudeSpectrum(); + const ValueList& Pitch(); + const ValueList& SpectralCentroid(); + const ValueList& SpectralSpread(); + const std::vector<ValueList>& SpectralFlux(); + const ValueList& Energy(); + const std::vector<ValueList>& MFCCs(); + const std::vector<ValueList>& DeltaMFCCs(); + const std::vector<ValueList>& DoubleDeltaMFCCs(); + +private: + void GenerateWindow(); +}; + +} // dsp namespace
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/src/file_comparer.cpp Thu Aug 25 11:05:55 2011 +0100 @@ -0,0 +1,427 @@ +// Copyright 2011, Ian Hobson. +// +// This file is part of gpsynth. +// +// gpsynth is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// gpsynth is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with gpsynth in the file COPYING. +// If not, see http://www.gnu.org/licenses/. + +#include "file_comparer.hpp" + +#include "boost_ex.hpp" +#include "statistics.hpp" +#include "std_ex.hpp" + +#include "boost/bind.hpp" +#include "boost/algorithm/string/classification.hpp" +#include "boost/algorithm/string/split.hpp" +#include "boost/math/special_functions/fpclassify.hpp" + +#include <algorithm> +#include <cmath> +#include <cstddef> +#include <iostream> +#include <iterator> + +// maps the frequency range 20-20480Hz to the 0-1 range on a log scale +// with 0.1 = 1 octave +namespace { + +dsp::Value ConvertFrequency(dsp::Value frequency) { + frequency = stdx::Clamp(frequency, 20.0, 20480.0); + return (::log2(frequency / 440.0) * 10 + 44.5943) / 100.0; +} + +void ConvertFrequencyList(dsp::ValueList& frequencies) { + std::transform(frequencies.begin(), frequencies.end(), frequencies.begin(), + &ConvertFrequency); +} + +} // namespace + +namespace dsp { + +// The FeatureComparer class abstracts common code required by feature set +// comparisons. +template<typename T> +class FeatureComparer : public FeatureComparerInterface { +public: + typedef void (*ConversionFunction)(T&); + typedef const T& (FeatureExtractor::*FeatureFunction)(); + typedef typename T::value_type ValueT; + typedef typename T::const_iterator IteratorT; + +private: + // + FeatureExtractor* extractor_; + // A member function of FeatureExtractor that retrieves an audio feature + FeatureFunction feature_function_; + // Stores the target feature for comparison + T target_buffer_; + // Temporary use stored as member to prevent reallocations + T comparison_buffer_; + // Used if a feature data set needs to have a conversion applied to it + ConversionFunction converter_; + // the feature's ID + int id_; + // true if the feature should be scaled by the frame's energy + bool scale_by_energy_; + +public: + FeatureComparer(int id, + FeatureExtractor* extractor, + FeatureFunction feature_function, + ConversionFunction converter = NULL, + bool scale_by_energy = false) + : id_(id), + extractor_(extractor), + feature_function_(feature_function), + converter_(converter), + scale_by_energy_(scale_by_energy) + {} + + // copy constructor + FeatureComparer(const FeatureComparer& other) + : id_(other.id_), + extractor_(other.extractor_), + feature_function_(other.feature_function_), + converter_(other.converter_), + scale_by_energy_(other.scale_by_energy_), + target_buffer_(other.target_buffer_) // copy the target value buffer + // no need to copy comparison buffer + {} + + virtual void AnalyzeTarget() { + // retrieve the target feature + target_buffer_ = (extractor_->*feature_function_)(); + if (converter_ != NULL) { + converter_(target_buffer_); + } + if (scale_by_energy_) { + std::transform(target_buffer_.begin(), target_buffer_.end(), + extractor_->Energy().begin(), + target_buffer_.begin(), + std::multiplies<ValueT>()); + } + } + + virtual Value Compare() { + // get the values to compare + const ValueList& feature = (extractor_->*feature_function_)(); + ValueList* feature_buffer; + if (converter_ != NULL || scale_by_energy_) { + comparison_buffer_ = feature; + if (converter_ != NULL) { + converter_(comparison_buffer_); + } + if (scale_by_energy_) { + std::transform(comparison_buffer_.begin(), comparison_buffer_.end(), + extractor_->Energy().begin(), + comparison_buffer_.begin(), + std::multiplies<ValueT>()); + } + feature_buffer = &comparison_buffer_; + } else { + feature_buffer = const_cast<ValueList*>(&feature); + } + // call conversion function + + // find range of buffer to compare against target + IteratorT buffer_start = feature_buffer->begin(); + IteratorT buffer_end; + if (feature_buffer->size() > target_buffer_.size()) { + buffer_end = buffer_start + target_buffer_.size(); + } else { + buffer_end = feature_buffer->end(); + } + // return root mean square error of buffer differences + return std::sqrt(stats::MeanSquaredError(buffer_start, + buffer_end, + target_buffer_.begin())); + } + + virtual FeatureComparerInterface* Clone() const { + return new FeatureComparer<T>(*this); + } + + virtual void SetExtractor(FeatureExtractor* extractor) { + extractor_ = extractor; + } + + virtual int ID() const { return id_; } +}; + +// specializations for std::vector<ValueList> +template<> +void FeatureComparer<std::vector<ValueList> >::AnalyzeTarget() { + target_buffer_ = (extractor_->*feature_function_)(); + if (converter_ != NULL) { + converter_(target_buffer_); + } + // for vector features we can scale by energy on a frame by frame basis +} + +template<> +Value FeatureComparer<std::vector<ValueList> >::Compare() { + // get the values to compare + const std::vector<ValueList>& feature = (extractor_->*feature_function_)(); + std::vector<ValueList>* feature_buffer; + // call conversion function + if (converter_ != NULL) { + comparison_buffer_ = feature; + converter_(comparison_buffer_); + feature_buffer = &comparison_buffer_; + } else { + // this isn't pretty, but it's the only way to allow avoiding a copy + // when no conversion is taking place.. + feature_buffer = const_cast<std::vector<ValueList>*>(&feature); + } + // find how many frames to compare + std::size_t frames_to_compare; + if (feature_buffer->size() > target_buffer_.size()) { + frames_to_compare = target_buffer_.size(); + } else { + frames_to_compare = feature_buffer->size(); + } + // take average of RMSE over all frames + Value error = 0; + const ValueList& energy = extractor_->Energy(); + if (scale_by_energy_) { + for (std::size_t i = 0; i < frames_to_compare; ++i) { + Value frame_error; + frame_error = std::sqrt(stats::MeanSquaredError(feature_buffer->at(i), + target_buffer_[i])); + error += frame_error * energy[i]; + } + } else { + for (std::size_t i = 0; i < frames_to_compare; ++i) { + error += std::sqrt(stats::MeanSquaredError(feature_buffer->at(i), + target_buffer_[i])); + } + } + return error / frames_to_compare; +} + + +FileComparer::FileComparer(int window_size /* = 1024 */, + int hop_size /* = 256 */) +: extractor_("", window_size, hop_size), + target_duration_(0) +{ + EnableFeature(Feature::LogMagnitude); + EnableFeature(Feature::Pitch); +} + +FileComparer::FileComparer(const std::string& feature_list, + int window_size /* = 1024 */, + int hop_size /* = 256 */) +: extractor_("", window_size, hop_size), + target_duration_(0) +{ + feature_names_["pitch"] = Feature::Pitch; + feature_names_["energy"] = Feature::Energy; + feature_names_["mfccs"] = Feature::MFCCs; + feature_names_["dmfccs"] = Feature::DeltaMFCCs; + feature_names_["ddmfccs"] = Feature::DoubleDeltaMFCCs; + feature_names_["mag"] = Feature::Magnitude; + feature_names_["logmag"] = Feature::LogMagnitude; + feature_names_["centroid"] = Feature::SpectralCentroid; + feature_names_["spread"] = Feature::SpectralSpread; + feature_names_["flux"] = Feature::SpectralFlux; + EnableFeatures(feature_list); +} + +FileComparer::FileComparer(const FileComparer& other) +: target_file_(other.target_file_), + target_duration_(other.target_duration_), + extractor_("", + other.extractor_.WindowSize(), + other.extractor_.HopSize()) +{ + foreach (const FeatureComparerPtr& pointer, other.features_) { + // clone the feature comparer + features_.push_back(FeatureComparerPtr(pointer->Clone())); + features_.back()->SetExtractor(&extractor_); + } +} + +void FileComparer::SetFeatureExtractorSettings(int window_size, int hop_size) { + bool reload_target = false; + if (extractor_.WindowSize() != window_size) { + extractor_.SetWindowSize(window_size); + reload_target = true; + } + if (extractor_.HopSize() != hop_size) { + extractor_.SetHopSize(hop_size); + reload_target = true; + } + if (reload_target && !target_file_.empty()) { + SetTargetFile(target_file_); + } +} + +void FileComparer::SetTargetFile(const std::string& target_file) { + if (target_file_ != target_file) { + target_file_ = target_file; + extractor_.LoadFile(target_file); + target_duration_ = extractor_.Duration(); + std::for_each(features_.begin(), features_.end(), + boost::bind(&FeatureComparerInterface::AnalyzeTarget, _1)); + } +} + + +Value FileComparer::CompareFile(const std::string& file_path) { + extractor_.LoadFile(file_path); + Value error; + foreach (FeatureComparerPtr& feature, features_) { + error += feature->Compare(); + } + return error / features_.size(); +} + +namespace { + +// Helper function for creating FeatureComparers +template<typename T> +FeatureComparerPtr MakeFeatureComparer(FileComparer::Feature::ID id, + FeatureExtractor& extractor, + const T& (FeatureExtractor::*feature)(), + void (*converter)(T&) = NULL, + bool scale_by_energy = false) { + return FeatureComparerPtr(new FeatureComparer<T>(static_cast<int>(id), + &extractor, + feature, + converter, + scale_by_energy)); +} + +} // namespace + +void FileComparer::EnableFeature(Feature::ID feature_id, bool enable) { + // check if the requested feature is already enabled + for (std::vector<FeatureComparerPtr>::iterator feature = features_.begin(), + end = features_.end(); + feature != end; + ++feature) { + if ((*feature)->ID() == feature_id) { + // feature enabled, if enable == false then remove the feature + if (!enable) { + features_.erase(feature); + } + return; + } + } + // make the feature comparer + FeatureComparerPtr feature; + switch (feature_id) { + case Feature::Pitch: + feature = MakeFeatureComparer(feature_id, + extractor_, + &FeatureExtractor::Pitch, + ConvertFrequencyList, + true); + break; + case Feature::Energy: + feature = MakeFeatureComparer(feature_id, + extractor_, + &FeatureExtractor::Energy); + break; + case Feature::MFCCs: + feature = MakeFeatureComparer(feature_id, + extractor_, + &FeatureExtractor::MFCCs); + break; + case Feature::DeltaMFCCs: + feature = MakeFeatureComparer(feature_id, + extractor_, + &FeatureExtractor::DeltaMFCCs); + break; + case Feature::DoubleDeltaMFCCs: + feature = MakeFeatureComparer(feature_id, + extractor_, + &FeatureExtractor::DoubleDeltaMFCCs); + break; + case Feature::Magnitude: + feature = MakeFeatureComparer(feature_id, + extractor_, + &FeatureExtractor::MagnitudeSpectrum); + case Feature::LogMagnitude: + feature = MakeFeatureComparer(feature_id, + extractor_, + &FeatureExtractor::LogMagnitudeSpectrum); + break; + case Feature::SpectralCentroid: + feature = MakeFeatureComparer(feature_id, + extractor_, + &FeatureExtractor::SpectralCentroid, + ConvertFrequencyList, + true); + break; + case Feature::SpectralSpread: + feature = MakeFeatureComparer(feature_id, + extractor_, + &FeatureExtractor::SpectralSpread, + ConvertFrequencyList, + true); + break; + case Feature::SpectralFlux: + feature = MakeFeatureComparer(feature_id, + extractor_, + &FeatureExtractor::SpectralFlux); + break; + default: + throw std::runtime_error("FileComparer::EnableFeature - Invalid ID"); + } + // analyze the feature if we already have a target file loaded + if (!target_file_.empty()) { + feature->AnalyzeTarget(); + } + // store the feature + features_.push_back(feature); +} + +void FileComparer::EnableFeatures(const std::vector<Feature::ID>& features, + bool enable /* = true */) { + foreach (Feature::ID id, features) { + EnableFeature(id, enable); + } +} + +void FileComparer::EnableFeatures(std::string feature_name_list, + bool enable /* = true */) { + std::vector<std::string> feature_names; + // split the comma separated list of names + boost::algorithm::split(feature_names, + feature_name_list, + boost::algorithm::is_any_of(", "), + boost::algorithm::token_compress_on); + // enable each feature + foreach (const std::string& feature_name, feature_names) { + // check if the requested feature name is valid + if (feature_names_.find(feature_name) == feature_names_.end()) { + std::stringstream message; + message << "FileComparer::EnableFeatures - Unknown feature '" + << feature_name << "'"; + throw std::runtime_error(message.str()); + } + EnableFeature(feature_names_[feature_name], enable); + } +} + +void FileComparer::SetFeatures(const std::vector<Feature::ID>& features) { + features_.clear(); + EnableFeatures(features); +} + +} // dsp namespace
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/src/file_comparer.hpp Thu Aug 25 11:05:55 2011 +0100 @@ -0,0 +1,94 @@ +// Copyright 2011, Ian Hobson. +// +// This file is part of gpsynth. +// +// gpsynth is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// gpsynth is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with gpsynth in the file COPYING. +// If not, see http://www.gnu.org/licenses/. + +// FileComparer - Compares sounds against a target set of audio features that +// have been retrieved from FeatureExtractor + +#pragma once + +#include "feature_extractor.hpp" + +#include "boost/shared_ptr.hpp" + +#include <map> +#include <string> +#include <vector> + +namespace dsp { + +struct FeatureComparerInterface { + virtual ~FeatureComparerInterface() {} + + virtual void AnalyzeTarget() = 0; + virtual Value Compare() = 0; + virtual FeatureComparerInterface* Clone() const = 0; + virtual void SetExtractor(FeatureExtractor* extractor) = 0; + virtual int ID() const = 0; +}; + +typedef boost::shared_ptr<FeatureComparerInterface> FeatureComparerPtr; + + +class FileComparer { +public: + + struct Feature { + enum ID { + Pitch, + Energy, + MFCCs, + DeltaMFCCs, + DoubleDeltaMFCCs, + Magnitude, + LogMagnitude, + SpectralCentroid, + SpectralSpread, + SpectralFlux + }; + }; + +private: + std::string target_file_; + Value target_duration_; + std::vector<FeatureComparerPtr> features_; + FeatureExtractor extractor_; + std::map<std::string, Feature::ID> feature_names_; + +public: + // copy constructor + FileComparer(const FileComparer& other); + FileComparer(int window_size = 1024, + int hop_size = 256); + FileComparer(const std::string& feature_list, + int window_size = 1024, + int hop_size = 256); + + void SetTargetFile(const std::string& target_file); + Value CompareFile(const std::string& file_path); + void SetFeatureExtractorSettings(int window_size, int hop_size); + + Value TargetDuration() const { return target_duration_; } + + void EnableFeature(Feature::ID feature, bool enable = true); + void EnableFeatures(const std::vector<Feature::ID>& features, + bool enable = true); + void EnableFeatures(std::string feature_name_list, bool enable = true); + void SetFeatures(const std::vector<Feature::ID>& features); +}; + +} // dsp namespace
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/src/grammar.hpp Thu Aug 25 11:05:55 2011 +0100 @@ -0,0 +1,44 @@ +// Copyright 2011, Ian Hobson. +// +// This file is part of gpsynth. +// +// gpsynth is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// gpsynth is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with gpsynth in the file COPYING. +// If not, see http://www.gnu.org/licenses/. + +// The Grammar interface. + +// TODO split out mutation behaviour in to new class + +#pragma once + +#include "synth_graph.hpp" + +struct GrammarInterface { + + virtual void RandomGraph(sg::Graph& graph) const = 0; + + //virtual sg::Graph MutateGraph(const sg::Graph& graph) const = 0; + + virtual void PickCrossoverNodes(const sg::Graph& parent_1, + const sg::Graph& parent_2, + sg::Vertex* node_1, + sg::Vertex* node_2) const = 0; + + virtual bool MutationAddSubTree(sg::Graph& graph) const = 0; + virtual bool MutationReplaceSubTree(sg::Graph& graph) const = 0; + virtual bool MutationModifyInputs(sg::Graph& graph) const = 0; + virtual bool MutationModifyConnections(sg::Graph& graph) const = 0; + virtual bool MutationReplaceCommand(sg::Graph& graph) const = 0; + virtual bool MutationInsertCommand(sg::Graph& graph) const = 0; +};
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/src/graph_helpers.hpp Thu Aug 25 11:05:55 2011 +0100 @@ -0,0 +1,104 @@ +// Copyright 2011, Ian Hobson. +// +// This file is part of gpsynth. +// +// gpsynth is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// gpsynth is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with gpsynth in the file COPYING. +// If not, see http://www.gnu.org/licenses/. + +// Some helper functions for boost.graph + +#pragma once + +#include "boost/graph/adjacency_list.hpp" +#include "boost/tuple/tuple.hpp" + +// removes the tree starting at the specified edge from the graph +template<typename GRAPH> +void RemoveSubTree(typename boost::graph_traits<GRAPH>::edge_descriptor edge, + GRAPH& graph) { + typename boost::graph_traits<GRAPH>::vertex_descriptor source; + typename boost::graph_traits<GRAPH>::vertex_descriptor target; + source = boost::source(edge, graph); + target = boost::target(edge, graph); + // avoid iteration as removing edges invalidates iterators + while (boost::in_degree(source, graph) > 0) { + RemoveSubTree(*(boost::in_edges(source, graph).first), graph); + } + boost::remove_edge(source, target, graph); + boost::remove_vertex(source, graph); +} + +// removes the tree starting at the specified vertex from the graph +template<typename T> +void RemoveSubTree(typename boost::graph_traits<T>::vertex_descriptor vertex, + T& graph) { + // avoid iteration as removing edges invalidates iterators + while (boost::in_degree(vertex, graph) > 0) { + RemoveSubTree(*(boost::in_edges(vertex, graph).first), graph); + } + boost::remove_vertex(vertex, graph); +} + +// inserts the subtree identified by source in to target_graph +// returns the root vertex of the inserted subtree +template<typename GRAPH> +typename boost::graph_traits<GRAPH>::vertex_descriptor +InsertSubTree(typename boost::graph_traits<GRAPH>::vertex_descriptor source, + const GRAPH& source_graph, + GRAPH& target_graph) { + typedef typename boost::graph_traits<GRAPH>::vertex_descriptor Vertex; + typedef typename boost::graph_traits<GRAPH>::in_edge_iterator InEdge; + Vertex new_vertex = boost::add_vertex(source_graph[source], target_graph); + InEdge in_edge; + InEdge in_edges_end; + boost::tie(in_edge, in_edges_end) = boost::in_edges(source, source_graph); + while (in_edge != in_edges_end) { + Vertex input = InsertSubTree(boost::source(*in_edge, source_graph), + source_graph, + target_graph); + boost::add_edge(input, new_vertex, source_graph[*in_edge], target_graph); + ++in_edge; + } + return new_vertex; +} + +// SwapSubTrees - swaps the subtrees starting at node_1 and node_2 +// writing to graphs passed in as targets +template<typename GRAPH> +void SwapSubTrees(typename boost::graph_traits<GRAPH>::vertex_descriptor node_1, + typename boost::graph_traits<GRAPH>::vertex_descriptor node_2, + const GRAPH& source_1, const GRAPH& source_2, + GRAPH& target_1, GRAPH& target_2) { + + typedef typename boost::graph_traits<GRAPH>::vertex_descriptor Vertex; + typedef typename boost::graph_traits<GRAPH>::edge_descriptor Edge; + // copy sources to targets + target_1 = source_1; + target_2 = source_2; + // get connecting output edges + Edge edge_1 = *(boost::out_edges(node_1, source_1).first); + Edge edge_2 = *(boost::out_edges(node_2, source_2).first); + // get connecting nodes on other end of outputs + Vertex target_node_1 = boost::target(edge_1, source_1); + Vertex target_node_2 = boost::target(edge_2, source_2); + // delete subtrees + RemoveSubTree(edge_1, target_1); + RemoveSubTree(edge_2, target_2); + // insert copies of subtrees in opposing target graphs + Vertex new_node_1 = InsertSubTree(node_2, source_2, target_1); + Vertex new_node_2 = InsertSubTree(node_1, source_1, target_2); + // connect the new subtrees using the same edge properties as old connection + boost::add_edge(new_node_1, target_node_1, source_1[edge_1], target_1); + boost::add_edge(new_node_2, target_node_2, source_2[edge_2], target_2); +}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/src/logger.cpp Thu Aug 25 11:05:55 2011 +0100 @@ -0,0 +1,64 @@ +// Copyright 2011, Ian Hobson. +// +// This file is part of gpsynth. +// +// gpsynth is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// gpsynth is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with gpsynth in the file COPYING. +// If not, see http://www.gnu.org/licenses/. + +#include "logger.hpp" + +namespace logger { + +Logger::Logger() +: output_stream_(std::cout), + quit_thread_(false) { + logging_thread_.reset(new boost::thread(&Logger::WorkerThread, this)); +} + +Logger::~Logger() { + quit_thread_ = true; + queue_condition_.notify_one(); + logging_thread_->join(); +} + +void Logger::LogMessage(const std::string& message) { + boost::mutex::scoped_lock lock(queue_mutex_); + log_queue_.push(message); + queue_condition_.notify_one(); +} + +void Logger::WorkerThread() { + while (!quit_thread_) { + boost::mutex::scoped_lock lock(queue_mutex_); + while (log_queue_.empty() && !quit_thread_) { + queue_condition_.wait(lock); + } + while (!log_queue_.empty()) { + output_stream_ << log_queue_.front(); + log_queue_.pop(); + } + output_stream_ << std::flush; + queue_condition_.notify_one(); + } +} + +void Logger::Flush() { + // wait for the worker thread to empty the work queue + boost::mutex::scoped_lock lock(queue_mutex_); + while (!log_queue_.empty()) { + queue_condition_.wait(lock); + } +} + +} // logger namespace
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/src/logger.hpp Thu Aug 25 11:05:55 2011 +0100 @@ -0,0 +1,63 @@ +// Copyright 2011, Ian Hobson. +// +// This file is part of gpsynth. +// +// gpsynth is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// gpsynth is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with gpsynth in the file COPYING. +// If not, see http://www.gnu.org/licenses/. + +// Simple threadsafe logger class + +#pragma once + +#include "boost/shared_ptr.hpp" +#include "boost/thread.hpp" + +#include <iostream> +#include <queue> +#include <string> + +namespace logger { + +class Logger { + std::ostream& output_stream_; + std::queue<std::string> log_queue_; + boost::mutex queue_mutex_; + boost::shared_ptr<boost::thread> logging_thread_; + boost::condition_variable queue_condition_; + bool quit_thread_; + +public: + Logger(); + ~Logger(); + void LogMessage(const std::string& message); + void Flush(); + + static Logger& Singleton() { + static Logger singleton; + return singleton; + } + +private: + void WorkerThread(); +}; + +inline void Log(const std::string& message) { + Logger::Singleton().LogMessage(message); +} + +inline void Flush() { + Logger::Singleton().Flush(); +} + +} // logger namespace
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/src/main.cpp Thu Aug 25 11:05:55 2011 +0100 @@ -0,0 +1,162 @@ +// Copyright 2011, Ian Hobson. +// +// This file is part of gpsynth. +// +// gpsynth is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// gpsynth is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with gpsynth in the file COPYING. +// If not, see http://www.gnu.org/licenses/. + +#include "file_comparer.hpp" +#include "graph_helpers.hpp" +#include "logger.hpp" +#include "population.hpp" +#include "program_options.hpp" +#include "sc_converter.hpp" +#include "sc_default_grammar.hpp" +#include "sc_evaluator.hpp" +#include "sc_grammar.hpp" + +#include "boost/filesystem.hpp" +#include "boost/shared_array.hpp" + +#include <algorithm> +#include <ctime> +#include <fstream> +#include <iostream> +#include <sstream> + +const std::string g_test_grammar_path = "/tmp/sc.json"; + +template<typename T> +void SaveToFile(const T& value, const std::string& file_path) { + std::ofstream file(file_path.c_str()); + file << value; +} + +void TestCrossover() { + sc::Grammar grammar(g_test_grammar_path); + sc::Converter converter(grammar); + sg::Graph parent_1; + sg::Graph parent_2; + grammar.SetMaximumDepth(2); + grammar.RandomGraph(parent_1); + grammar.RandomGraph(parent_2); + sg::Vertex crossover_1; + sg::Vertex crossover_2; + grammar.PickCrossoverNodes(parent_1, parent_2, &crossover_1, &crossover_2); + // swap the subtrees + sg::Graph child_1; + sg::Graph child_2; + SwapSubTrees(crossover_1, crossover_2, parent_1, parent_2, child_1, child_2); + SaveToFile(converter.ToDOT(parent_1), "/tmp/parent1.dot"); + SaveToFile(converter.ToDOT(parent_2), "/tmp/parent2.dot"); + SaveToFile(converter.ToDOT(child_1), "/tmp/child1.dot"); + SaveToFile(converter.ToDOT(child_2), "/tmp/child2.dot"); +} + +void TestMutationReplaceSubtree() { + sc::Grammar grammar(g_test_grammar_path); + sc::Converter converter(grammar); + sg::Graph graph; + grammar.RandomGraph(graph); + SaveToFile(converter.ToDOT(graph), "/tmp/mutation_str_before.dot"); + grammar.MutationReplaceSubTree(graph); + SaveToFile(converter.ToDOT(graph), "/tmp/mutation_str_after.dot"); +} + +void TestMutationReplaceCommand() { + sc::Grammar grammar(g_test_grammar_path); + sc::Converter converter(grammar); + sg::Graph graph; + grammar.SetMaximumDepth(3); + grammar.RandomGraph(graph); + SaveToFile(converter.ToDOT(graph), "/tmp/mutation_str_before.dot"); + SaveToFile(converter.ToSynthDef(graph, "before"), + "/tmp/mutation_str_before.sc"); + grammar.MutationReplaceCommand(graph); + SaveToFile(converter.ToDOT(graph), "/tmp/mutation_str_after.dot"); + SaveToFile(converter.ToSynthDef(graph, "after"), + "/tmp/mutation_str_after.sc"); +} + +void TestMutationInsertCommand() { + sc::Grammar grammar(g_test_grammar_path); + sc::Converter converter(grammar); + sg::Graph graph; + grammar.SetMaximumDepth(4); + grammar.RandomGraph(graph); + SaveToFile(converter.ToDOT(graph), "/tmp/mutation_str_before.dot"); + SaveToFile(converter.ToSynthDef(graph, "before"), + "/tmp/mutation_str_before.sc"); + grammar.MutationInsertCommand(graph); + SaveToFile(converter.ToDOT(graph), "/tmp/mutation_str_after.dot"); + SaveToFile(converter.ToSynthDef(graph, "after"), + "/tmp/mutation_str_after.sc"); +} + +void DoEvolution(const ProgramOptions& settings) { + // prepare the file comparer + dsp::FileComparer target(settings.fitness_features_, + static_cast<int>(settings.analysis_window_size_), + static_cast<int>(settings.analysis_hop_size_)); + target.SetTargetFile(settings.target_path_); + // prepare the supercollider objects + std::string grammar_json; + if (settings.grammar_path_.empty()) { + grammar_json.assign(sc::g_default_grammar); + } else { + grammar_json = stdx::LoadFile(settings.grammar_path_); + } + sc::Grammar grammar(grammar_json); + grammar.SetMaximumDepth(static_cast<int>(settings.maximum_tree_depth_)); + sc::Evaluator evaluator(grammar, + settings.sc_app_path_, + target, + static_cast<int>(settings.core_limit_)); + sc::Converter converter(grammar); + // set up the population + Population population(&grammar, &converter, &evaluator); + population.SetWorkFolder(settings.work_folder_); + population.SetFitnessThreshold(settings.fitness_threshold_); + population.SetCrossoverRate(settings.crossover_rate_); + population.SetMutationRate(settings.mutation_rate_); + population.SetTournamentSize(static_cast<int>(settings.tournament_size_)); + population.SetReproduceBest(settings.reproduce_best_individual_); + population.InitializePopulation(settings.population_size_); + population.SetKeepTempFolders(settings.keep_temp_folders_); + // run the evolution loop + population.Evolve(static_cast<int>(settings.generations_)); +} + +int main(int argument_count, const char* arguments[]) +{ + srand(static_cast<unsigned int>(time(NULL))); + try { + // parse the command line options + ProgramOptions settings; + ParseCommandLine(argument_count, arguments, &settings); + DoEvolution(settings); + + //TestCrossover(); + //TestMutationReplaceSubtree(); + //TestMutationInsertCommand(); + //TestMutationReplaceCommand(); + } catch (const std::exception& e) { + std::stringstream message; + message << "Exception: " << e.what() << '\n'; + logger::Log(message.str()); + logger::Flush(); + return -1; + } + logger::Flush(); +}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/src/mfcc_analyzer.cpp Thu Aug 25 11:05:55 2011 +0100 @@ -0,0 +1,143 @@ +// Copyright 2011, Ian Hobson. +// +// This file is part of gpsynth. +// +// gpsynth is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// gpsynth is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with gpsynth in the file COPYING. +// If not, see http://www.gnu.org/licenses/. + +#include "mfcc_analyzer.hpp" + +#include "boost_ex.hpp" + +#include <algorithm> +#include <cmath> +#include <iostream> +#include <iterator> +#include <limits> +#include <numeric> + +namespace { + +// using the scale described in the HTK book +double MelToHz(double mel) { + return 700.0 * (std::pow(10.0, mel / 2595.0) - 1.0); +} + +double HzToMel(double hz) { + return 2595.0 * std::log10(1.0 + (hz / 700.0)); +} + +} // namespace + +namespace dsp { + +MFCCAnalyzer::MFCCAnalyzer(int frame_size, + int mel_bands, + int number_of_mfccs, + double frequency_min, + double frequency_max) +: frame_size_(frame_size), + mel_bands_(mel_bands), + number_of_mfccs_(number_of_mfccs), + sample_rate_(0), + frequency_min_(frequency_min), + frequency_max_(frequency_max) +{ + InitializeMelDCT(); +} + +MFCCAnalyzer::~MFCCAnalyzer() { + fftw_free(mel_bands_dct_in_); + fftw_free(mel_bands_dct_out_); + fftw_destroy_plan(dct_plan_); +} + +void MFCCAnalyzer::SetSampleRate(double sample_rate) { + if (sample_rate_ != sample_rate) { + sample_rate_ = sample_rate; + InitializeFilters(); + } +} + +// extracts mfccs from a magnitude spectrum +const std::vector<double>& +MFCCAnalyzer::ExtractMFCCS(const std::vector<double>& spectrum) { + // filter spectrum + for (std::size_t i = 0; i < mel_bands_; i++) { + mel_bands_dct_in_[i] = std::inner_product(spectrum.begin(), + spectrum.end(), + mel_filters_[i].begin(), + 0.0); + } + // take the logs of the filter outputs (avoiding log(0) == -inf) + std::replace(mel_bands_dct_in_, mel_bands_dct_in_ + mel_bands_, + 0.0, std::numeric_limits<double>::min()); + std::transform(mel_bands_dct_in_, mel_bands_dct_in_ + mel_bands_, + mel_bands_dct_in_, + static_cast<double(*)(double)>(std::log10)); + // take the DCT of the mel bands + fftw_execute(dct_plan_); + // store result + mfccs_.assign(mel_bands_dct_out_, mel_bands_dct_out_ + number_of_mfccs_); + return mfccs_; +} + +void MFCCAnalyzer::InitializeMelDCT() { + int mem_size = sizeof(double) * mel_bands_; + mel_bands_dct_in_ = (double*)fftw_malloc(mem_size); + mel_bands_dct_out_ = (double*)fftw_malloc(mem_size); + dct_plan_ = fftw_plan_r2r_1d(number_of_mfccs_, + mel_bands_dct_in_, + mel_bands_dct_out_, + FFTW_REDFT10, + FFTW_ESTIMATE); +} + +void MFCCAnalyzer::InitializeFilters() { + // create a series of triangular filters mapped linearly on the mel-frequency + // scale. + double bin_frequency = sample_rate_ / (2 * frame_size_); + double mel_min = HzToMel(frequency_min_); + double mel_max = HzToMel(frequency_max_); + double mel_width = (mel_max - mel_min) / (mel_bands_ + 1.0); + double mel_left = mel_min; + mel_filters_.resize(mel_bands_); + for (int i = 0; i < mel_bands_; i++) { + double mel_centre = mel_left + mel_width; + double mel_right = mel_centre + mel_width; + double left_hz = MelToHz(mel_left); + double right_hz = MelToHz(mel_right); + int bin_left = left_hz / bin_frequency + 0.5; + int bin_right = right_hz / bin_frequency + 0.5; + int bin_centre = (bin_left + bin_right) / 2.0 + 0.5; + double filter_height = 2.0 / (bin_right - bin_left); + mel_filters_[i].assign(frame_size_, 0.0); + // generate the up ramp + double bin_delta = filter_height / (bin_centre - bin_left); + double ramp = 0.0; + for (int bin = bin_left; bin < bin_centre; bin++) { + mel_filters_[i][bin] = ramp; + ramp += bin_delta; + } + // down ramp + for (int bin = bin_centre; bin < bin_right; bin++) { + mel_filters_[i][bin] = ramp; + ramp -= bin_delta; + } + // move mel bin along + mel_left += mel_width; + } +} + +} // dsp namespace
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/src/mfcc_analyzer.hpp Thu Aug 25 11:05:55 2011 +0100 @@ -0,0 +1,61 @@ +// Copyright 2011, Ian Hobson. +// +// This file is part of gpsynth. +// +// gpsynth is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// gpsynth is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with gpsynth in the file COPYING. +// If not, see http://www.gnu.org/licenses/. + +// MFCCAnalyzer - extracts MFCCs from a provided magnitude spectrum + +#pragma once + +#include "fftw3.h" + +#include <vector> + +namespace dsp { + +class MFCCAnalyzer { + fftw_plan dct_plan_; + int frame_size_; + double sample_rate_; + double frequency_min_; + double frequency_max_; + std::vector< std::vector<double> > mel_filters_; + int mel_bands_; + int number_of_mfccs_; + std::vector<double> mfccs_; + double* mel_bands_dct_in_; + double* mel_bands_dct_out_; + +public: + MFCCAnalyzer(int frame_size, + int mel_bands, + int number_of_mfccs, + double frequency_min, + double frequency_max); + + ~MFCCAnalyzer(); + + void SetSampleRate(double sample_rate); + + // extracts mfccs from a magnitude spectrum + const std::vector<double>& ExtractMFCCS(const std::vector<double>& spectrum); + +private: + void InitializeMelDCT(); + void InitializeFilters(); +}; + +} // dsp namespace
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/src/population.cpp Thu Aug 25 11:05:55 2011 +0100 @@ -0,0 +1,397 @@ +// Copyright 2011, Ian Hobson. +// +// This file is part of gpsynth. +// +// gpsynth is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// gpsynth is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with gpsynth in the file COPYING. +// If not, see http://www.gnu.org/licenses/. + +#include "population.hpp" + +#include "boost_ex.hpp" +#include "graph_helpers.hpp" +#include "logger.hpp" +#include "statistics.hpp" +#include "std_ex.hpp" + +#include "boost/bind.hpp" +#include "boost/filesystem.hpp" +#include "boost/lexical_cast.hpp" +#include "boost/math/special_functions/fpclassify.hpp" + +#include <algorithm> +#include <cmath> +#include <fstream> +#include <iterator> +#include <limits> +#include <map> +#include <numeric> +#include <set> +#include <sstream> +#include <vector> + +namespace bf = boost::filesystem; + +namespace { + + const std::string g_default_work_folder = "/tmp"; + +// mutated graphs might have imported parameters outside of the graph's range +// or removed parameter inputs making some parameters redundant +void UpdateParameters(sg::Graph& graph) { + std::size_t parameter_count = graph[boost::graph_bundle].parameters_.size(); + // scan through vertices to find parameter inputs + std::map<int, std::vector<sg::Vertex> > parameters; + sg::VertexIterator vertex; + sg::VertexIterator vertex_end; + for (boost::tie(vertex, vertex_end) = boost::vertices(graph); + vertex != vertex_end; + ++vertex) { + const sg::Node& node_info = graph[*vertex]; + if (node_info.type_ == sg::kParameter) { + parameters[node_info.id_].push_back(*vertex); + } + } + // check if any corrections required + if (parameters.size() == parameter_count) { + if (parameter_count == 0) { + // no parameters in the graph, nothing to do + return; + } + // check that last parameter id is correct, otherwise corrections are needed + if (parameters.rbegin()->first == (parameter_count)) { + // all seems well, get out of here + return; + } + } else if (parameters.size() > parameter_count) { + // add extra parameters to the graph + std::generate_n(std::back_inserter(graph[boost::graph_bundle].parameters_), + parameters.size() - parameter_count, + stdx::RandomCoefficient); + } else { + // remove redundant parameters + graph[boost::graph_bundle].parameters_.resize(parameters.size()); + } + // reassign parameter ids through the graph + int parameter_id = 1; + typedef std::map<int, std::vector<sg::Vertex> >::value_type ParameterMapping; + foreach (ParameterMapping p, parameters) { + foreach (sg::Vertex v, p.second) { + graph[v].id_ = parameter_id; + } + ++parameter_id; + } + // job done +} + +} // namespace + +Population::Population(GrammarInterface* grammar, + ConverterInterface* converter, + EvaluatorInterface* evaluator) +: grammar_(grammar), + converter_(converter), + evaluator_(evaluator), + generation_(0), + max_generations_(0), + fitness_threshold_(0), + tournament_size_(7), + crossover_rate_(0.65), + mutation_rate_(0.3), + reproduce_best_(false), + keep_temp_folders_(false) +{ + evaluator_->SetListener(this); + mutators_.push_back(boost::bind(&GrammarInterface::MutationReplaceSubTree, + grammar, _1)); + mutators_.push_back(boost::bind(&GrammarInterface::MutationAddSubTree, + grammar, _1)); + mutators_.push_back(boost::bind(&GrammarInterface::MutationModifyInputs, + grammar, _1)); + mutators_.push_back(boost::bind(&GrammarInterface::MutationModifyConnections, + grammar, _1)); + mutators_.push_back(boost::bind(&GrammarInterface::MutationReplaceCommand, + grammar, _1)); +// mutators_.push_back(boost::bind(&GrammarInterface::MutationInsertCommand, +// grammar, _1)); + SetupGeneticOperationSelector(); + SetWorkFolder(g_default_work_folder); +} + +void Population::SetWorkFolder(const std::string &path) { + work_folder_ = path; + log_folder_ = path + "/logs"; + render_folder_ = path + "/render"; + dot_folder_ = path + "/dot"; + export_folder_ = path + "/export"; +} + +void Population::Evolve(int max_generations) { + max_generations_ = max_generations; + // make sure the folders for storing best outputs are created + bf::create_directories(render_folder_); + bf::create_directories(dot_folder_); + bf::create_directories(export_folder_); + bf::create_directories(log_folder_); + for (generation_ = 1; generation_ <= max_generations; generation_++) { + NewGenerationMessage(); + SetGenerationFolder(); + ExportToDOT(); + EvaluateGeneration(); + AnalyzeGeneration(); + SaveGenerationBest(); + CleanTempFolders(); + if (CheckThreshold()) { + logger::Log("Fitness threshold reached\n"); + return; + } + BreedNewGeneration(); + } +} + +void Population::NewGenerationMessage() const { + std::stringstream message; + message << "Generation " << generation_ << '\n'; + logger::Log(message.str()); +} + +void Population::SetGenerationFolder() { + std::stringstream path; + path << work_folder_ << "/generation_" + << std::setw(std::log10(max_generations_) + 1) << std::setfill('0') + << generation_ << '/'; + generation_folder_ = path.str(); +} + +bool Population::CheckThreshold() const { + double best_fitness = population_[best_fit_][boost::graph_bundle].fitness_; + return best_fitness <= fitness_threshold_; +} + +void Population::CleanTempFolders() const { + if (!keep_temp_folders_) { + bf::remove_all(generation_folder_); + } +} + +void Population::InitializePopulation(std::size_t size) { + population_.resize(size); + fitnesses_.resize(size); + for (int i = 0; i < size; i++) { + grammar_->RandomGraph(population_[i]); + population_[i][boost::graph_bundle].id_ = i; + } +} + +void Population::EvaluateGeneration() { + // make sure we have a clean temp folder for the generation + bf::remove_all(generation_folder_); + evaluator_->SetWorkFolder(generation_folder_); + evaluator_->RateGraphs(population_); + logger::Log("\n"); + // store graph fitnesses + for (std::size_t i = 0, size = population_.size(); i < size; i++) { + fitnesses_[i] = population_[i][boost::graph_bundle].fitness_; + } +} + +void Population::AnalyzeGeneration() { + double mean_fitness = stats::Mean(fitnesses_.begin(), fitnesses_.end()); + if (boost::math::isnan(mean_fitness)) { + logger::Log("ruh roh\n"); // TODO + } + std::stringstream message; + std::vector<double>::const_iterator best_fitness; + best_fitness = std::min_element(fitnesses_.begin(), fitnesses_.end()); + best_fit_ = static_cast<int>(best_fitness - fitnesses_.begin()); + message << "Average Fitness:" << mean_fitness + << " Best" + //<< " [" << best_fit_ << "]" + << ':' << *best_fitness << '\n'; + logger::Log(message.str()); + // save fitness values to logs + std::stringstream average; + average << generation_ << ' ' << mean_fitness; + stdx::AppendToFile(average.str(), log_folder_ + "/average_fitnesses.txt"); + std::stringstream best; + best << generation_ << ' ' << *best_fitness; + stdx::AppendToFile(best.str(), log_folder_ + "/best_fitnesses.txt"); +} + +void Population::SaveGenerationBest() { + std::stringstream generation_prefix; + generation_prefix << "generation_" + << std::setw(std::log10(max_generations_) + 1) + << std::setfill('0') + << generation_; + const sg::Graph& best_graph = population_[best_fit_]; + const sg::GraphProperties& graph_properties = best_graph[boost::graph_bundle]; + // copy the rendered output for the graph + bf::path path = graph_properties.render_path_; + if (bf::exists(path)) { + std::string render_path = render_folder_ + '/' + + generation_prefix.str() + + path.extension().string(); + bf::copy_file(path, render_path); + } + // copy the dot script for the graph + path = graph_properties.dot_path_; + if (bf::exists(path)) { + std::string dot_path = dot_folder_ + '/' + + generation_prefix.str() + + path.extension().string(); + bf::copy_file(path, dot_path); + } + // tell the converter to export the graph + converter_->Export(best_graph, export_folder_, generation_prefix.str()); +} + +void Population::PerformCrossover(sg::Graph& child_1, sg::Graph& child_2) { + int parent_id_1 = SelectIndividual(); + int parent_id_2; + do { + parent_id_2 = SelectIndividual(); + } while (parent_id_1 == parent_id_2); + // pick the crossover points via the grammar + const sg::Graph& parent_1 = population_[parent_id_1]; + const sg::Graph& parent_2 = population_[parent_id_2]; + sg::Vertex crossover_1; + sg::Vertex crossover_2; + grammar_->PickCrossoverNodes(parent_1, parent_2, &crossover_1, &crossover_2); + // swap the subtrees + SwapSubTrees(crossover_1, crossover_2, parent_1, parent_2, child_1, child_2); +} + +// called for mutated graphs to prepare for next generation +void Population::PrepareNewChild(sg::Graph& child) { + // reset fitness rating + child[boost::graph_bundle].fitness_ = sg::kFitnessUnrated; + // make sure parameters in graph are valid + UpdateParameters(child); +} + +void Population::BreedNewGeneration() { + // Used to store reproduced graph ids, prevents a graph being reproduced + // more than once. + std::set<int> reproduced; + std::vector<sg::Graph> next_generation; + next_generation.reserve(population_.size()); + if (reproduce_best_) { + next_generation.push_back(population_[best_fit_]); + } + std::size_t population_size = population_.size(); + while (next_generation.size() < population_size) { + GeneticOperation operation; + operation = static_cast<GeneticOperation>(genetic_operation_selector_()); + if (operation == kCrossover) { + // Crossover + // create two empty graphs to receive results of crossover + // (creating them directly in the next_generation container causes + // memory problems when performing crossover, couldn't fathom why..) + sg::Graph child_1; + sg::Graph child_2; + PerformCrossover(child_1, child_2); + PrepareNewChild(child_1); + PrepareNewChild(child_2); + next_generation.push_back(child_1); + next_generation.push_back(child_2); + } else if (operation == kMutation) { + // mutation + // store a copy of an individual from current population + next_generation.push_back(population_[SelectIndividual()]); + sg::Graph& child = next_generation.back(); + // call random mutation operator on child + bool mutated; + do { + // some mutators might not be able to operate on a specific graph + // and will return false, so try another mutator + mutated = mutators_[stdx::Random(mutators_.size())](child); + } while (!mutated); + PrepareNewChild(child); + } else { + // reproduction + int graph_id = SelectIndividual(); + // only reproduce the individual if it hasn't already been reproduced + if (reproduced.find(graph_id) == reproduced.end()) { + next_generation.push_back(population_[graph_id]); + reproduced.insert(graph_id); + } + } + } + // we might have an extra individual from crossover + next_generation.resize(population_size); + // reassign graph ids + for (std::size_t i = 0, size = population_.size(); i < size; i++) { + next_generation[i][boost::graph_bundle].id_ = static_cast<int>(i); + } + // replace the population with the newly created generation + population_.swap(next_generation); +} + +// tournament selection +int Population::SelectIndividual() { + // select n individuals from the population + std::set<std::size_t> tournament; + while (tournament.size() < tournament_size_) { + tournament.insert(stdx::Random(population_.size())); + } + double best_fitness = std::numeric_limits<double>::max(); + std::size_t result = -1; + // find best graph from selection + foreach (std::size_t graph_id, tournament) { + if (fitnesses_[graph_id] < best_fitness) { + best_fitness = fitnesses_[graph_id]; + result = graph_id; + } + } + return static_cast<int>(result); +} + +void Population::ExportToDOT() { + std::string dot_folder = generation_folder_ + "/dot/"; + bf::create_directories(dot_folder); + std::stringstream path; + foreach (sg::Graph& graph, population_) { + path.str(""); + path << dot_folder + << "gpsynth_" << graph[boost::graph_bundle].id_ << ".dot"; + std::ofstream file(path.str().c_str()); + file << converter_->ToDOT(graph); + file.close(); + graph[boost::graph_bundle].dot_path_ = path.str(); + } +} + +void Population::GraphRatedNotification(std::size_t graphs_rated) { + std::stringstream message; + message << "\rRated " << graphs_rated << " / " << population_.size(); + logger::Log(message.str()); +} + +void Population::SetCrossoverRate(double rate) { + crossover_rate_ = rate; + SetupGeneticOperationSelector(); +} + +void Population::SetMutationRate(double rate) { + mutation_rate_ = rate; + SetupGeneticOperationSelector(); +} + +void Population::SetupGeneticOperationSelector() { + std::vector<double> genetic_probabilities(2); + genetic_probabilities[kCrossover] = crossover_rate_; + genetic_probabilities[kMutation] = mutation_rate_; + // reproduction is left over probability + genetic_operation_selector_.SetProbabilities(genetic_probabilities); +}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/src/population.hpp Thu Aug 25 11:05:55 2011 +0100 @@ -0,0 +1,109 @@ +// Copyright 2011, Ian Hobson. +// +// This file is part of gpsynth. +// +// gpsynth is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// gpsynth is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with gpsynth in the file COPYING. +// If not, see http://www.gnu.org/licenses/. + +// Population class, manages a population of synth graphs and their evolution + +// TODO - split out evolution behaviour in to a separate class, support +// multiple populations + +#pragma once + +#include "converter.hpp" +#include "evaluator.hpp" +#include "grammar.hpp" +#include "statistics.hpp" +#include "synth_graph.hpp" + +#include "boost/function.hpp" + +#include <string> +#include <vector> + +typedef boost::function<bool (sg::Graph&)> MutationOperator; + +class Population : public EvaluatorListenerInterface { + std::vector<sg::Graph> population_; + int best_fit_; + std::vector<double> fitnesses_; + // current generation + int generation_; + // total generations + int max_generations_; + int tournament_size_; + double fitness_threshold_; + double crossover_rate_; + double mutation_rate_; + bool reproduce_best_; + bool keep_temp_folders_; + GrammarInterface* grammar_; + ConverterInterface* converter_; + EvaluatorInterface* evaluator_; + // work folder, where all files for current run are placed + std::string work_folder_; + // folder to write logs to + std::string log_folder_; + // folder to save best renders + std::string render_folder_; + // folder to save best dot scripts + std::string dot_folder_; + // folder to save exported graphs + std::string export_folder_; + // temp folder for current generation + std::string generation_folder_; + // available mutation operators + std::vector<MutationOperator> mutators_; + // selector for genetic operations + stats::ProbabilitySelector genetic_operation_selector_; + + enum GeneticOperation { + kCrossover, + kMutation, + kReproduction + }; + +public: + Population(GrammarInterface* grammar, + ConverterInterface* converter, + EvaluatorInterface* evaluator); + + void InitializePopulation(std::size_t size); + void SetWorkFolder(const std::string& path); + void SetFitnessThreshold(double value) { fitness_threshold_ = value; } + void SetTournamentSize(int size) { tournament_size_ = size; } + void Evolve(int generations); + void ExportToDOT(); + void SetCrossoverRate(double rate); + void SetMutationRate(double rate); + void SetReproduceBest(bool value) { reproduce_best_ = value; } + void SetKeepTempFolders(bool value) { keep_temp_folders_ = value; } + +private: + void EvaluateGeneration(); + void AnalyzeGeneration(); + void BreedNewGeneration(); + int SelectIndividual(); + void PerformCrossover(sg::Graph& child_1, sg::Graph& child_2); + void PrepareNewChild(sg::Graph& child); + void GraphRatedNotification(std::size_t graphs_rated); + void SetupGeneticOperationSelector(); + void SaveGenerationBest(); + bool CheckThreshold() const; + void CleanTempFolders() const; + void NewGenerationMessage() const; + void SetGenerationFolder(); +};
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/src/precompiled_header.hpp Thu Aug 25 11:05:55 2011 +0100 @@ -0,0 +1,62 @@ +// Copyright 2011, Ian Hobson. +// +// This file is part of gpsynth. +// +// gpsynth is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// gpsynth is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with gpsynth in the file COPYING. +// If not, see http://www.gnu.org/licenses/. + +// commonly used external libraries +// code should compile correctly without using precompiled header + +#include "boost/algorithm/string/replace.hpp" +#include "boost/bind.hpp" +#include "boost/date_time/posix_time/posix_time.hpp" +#define BOOST_FILESYSTEM_NO_DEPRECATED +#include "boost/filesystem.hpp" +#include "boost/foreach.hpp" +#include "boost/format.hpp" +#include "boost/function.hpp" +#include "boost/graph/adjacency_list.hpp" +#include "boost/graph/graphviz.hpp" +#include "boost/graph/random.hpp" +#include "boost/graph/topological_sort.hpp" +#include "boost/lexical_cast.hpp" +#include "boost/math/special_functions/fpclassify.hpp" +#include "boost/random/mersenne_twister.hpp" +#include "boost/random/normal_distribution.hpp" +#include "boost/random/variate_generator.hpp" +#include "boost/shared_array.hpp" +#include "boost/shared_ptr.hpp" +#include "boost/thread.hpp" +#include "boost/tuple/tuple.hpp" + +#include "fftw3.h" + +#include "json.h" + +#include <cmath> +#include <cstddef> +#include <ctime> +#include <iostream> +#include <iterator> +#include <fstream> +#include <limits> +#include <map> +#include <numeric> +#include <queue> +#include <set> +#include <sstream> +#include <stdexcept> +#include <string> +#include <vector>
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/src/program_options.cpp Thu Aug 25 11:05:55 2011 +0100 @@ -0,0 +1,180 @@ +// Copyright 2011, Ian Hobson. +// +// This file is part of gpsynth. +// +// gpsynth is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// gpsynth is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with gpsynth in the file COPYING. +// If not, see http://www.gnu.org/licenses/. + +#include "program_options.hpp" + +#include "boost/filesystem.hpp" +#include "boost/program_options.hpp" + +#include <ctime> + +namespace bf = boost::filesystem; +namespace bo = boost::program_options; + +namespace { + +const std::string g_default_sc_app_path = "/Applications/SuperCollider"; + +void PrintHelpAndQuit(const bo::options_description& options, + int exit_code = 0) { + bo::arg = "value"; + std::cout << '\n' << options << std::endl; + exit(exit_code); +} + +std::string GetTimeString() { + std::time_t raw_time; + time(&raw_time); + tm* time_info = localtime(&raw_time); + char buffer[20]; + strftime(buffer, 20, "%Y-%m-%d_%H:%M:%S", time_info); + return buffer; +} + + +} // namespace + + +void ParseCommandLine(int argument_count, + const char* arguments[], + ProgramOptions* options) { + bo::options_description description; + description.add_options() + ("help,?", "Displays this message") + ("target,t", + bo::value<std::string>(&options->target_path_)->required(), + "Target sound file path") + ("grammar,g", + bo::value<std::string>(&options->grammar_path_), + "Grammar file path") + ("scpath,s", + bo::value<std::string>(&options->sc_app_path_), + "Path to SuperCollider application folder") + ("workfolder,w", + bo::value<std::string>(&options->work_folder_), + "Path of folder to place files in") + ("population,p", + bo::value<std::size_t>(&options->population_size_)->default_value(500), + "Population size") + ("generations,G", + bo::value<std::size_t>(&options->generations_)->default_value(20), + "Number of Generations") + ("fitness,f", + bo::value<double>(&options->fitness_threshold_)->default_value(0), + "Fitness threshold") + ("tournament,T", + bo::value<std::size_t>(&options->tournament_size_)->default_value(7), + "Tournament selection size") + ("crossover,c", + bo::value<double>(&options->crossover_rate_)->default_value(0.6, "0.6"), + "Crossover rate") + ("mutation,m", + bo::value<double>(&options->mutation_rate_)->default_value(0.35, "0.35"), + "Mutation rate") + ("reproducebest,r", + bo::value<bool>(&options->reproduce_best_individual_)->default_value(false), + "Automatically reproduce best individual when populating next generation.") + ("features,F", + bo::value<std::string>(&options->fitness_features_), + "Set of features that the fitness function will measure when comparing files." + " Must be a comma separated list of feature names, which can be any of the following:" + " pitch, energy, mfccs, dmfccs, ddmfccs, mag, logmag, centroid, spread, flux." + " Default value is 'mfccs,dmfccs,ddmfccs'.") + ("windowsize,w", + bo::value<std::size_t>(&options->analysis_window_size_)->default_value(1024), + "Analysis Window Size") + ("hopsize,h", + bo::value<std::size_t>(&options->analysis_hop_size_)->default_value(256), + "Analysis Hop Size") + ("maxtreedepth,M", + bo::value<std::size_t>(&options->maximum_tree_depth_)->default_value(3), + "Maximum generated tree depth (can grow bigger through mutation).") + ("keepfolders,k", + bo::value<bool>(&options->keep_temp_folders_)->default_value(false), + "When set to 1 the temporary generation folders won't be deleted") + ("corelimit,C", + bo::value<std::size_t>(&options->core_limit_)->default_value(0), + "Limits the number of cores gpsynth will use simultaneously, 0=unlimited.") + ; + + bo::command_line_parser parser(argument_count, arguments); + parser.options(description); + bo::variables_map config; + try { + bo::store(parser.run(), config); + bo::notify(config); + } catch (std::exception& e) { + std::cout << "Error parsing options: " << e.what() << ".\n"; + PrintHelpAndQuit(description, -1); + } + + if (config.count("help")) { + PrintHelpAndQuit(description); + } + + if (config.count("workfolder") == 0) { + options->work_folder_ = bf::current_path().string(); + } + // validate the work folder path + if (!bf::is_directory(options->work_folder_)) { + std::cout << "Work folder path '" << options->work_folder_ + << "' doesn't exist.\n"; + exit(-1); + } + options->work_folder_ += "/gpsynth_" + GetTimeString() + "/"; + + if (config.count("scpath") == 0) { + options->sc_app_path_ = g_default_sc_app_path; + } + if (!bf::is_directory(options->sc_app_path_)) { + std::cout << "SC App path '" << options->sc_app_path_ + << "' doesn't exist.\n"; + exit(-1); + } + + if (options->tournament_size_ < 1) { + std::cout << "Tournament size must be greater than zero.\n"; + exit(-1); + } + + if (options->tournament_size_ > options->population_size_) { + std::cout << "Tournament size can not be larger than population size.\n"; + exit(-1); + } + + if (options->crossover_rate_ < 0 || options->crossover_rate_ > 1) { + std::cout << "The crossover rate should be between 0-1.\n"; + exit(-1); + } + + if (options->mutation_rate_ < 0 || options->mutation_rate_ > 1) { + std::cout << "The mutation rate should be between 0-1.\n"; + exit(-1); + } + + if (options->maximum_tree_depth_ < 1) { + std::cout << "Maximum tree depth must be greater than zero.\n"; + exit(-1); + } + + if (options->fitness_features_.empty()) { + options->fitness_features_ = "mfccs,dmfccs,ddmfccs"; + } +} + +
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/src/program_options.hpp Thu Aug 25 11:05:55 2011 +0100 @@ -0,0 +1,47 @@ +// Copyright 2011, Ian Hobson. +// +// This file is part of gpsynth. +// +// gpsynth is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// gpsynth is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with gpsynth in the file COPYING. +// If not, see http://www.gnu.org/licenses/. + +// command line options handler + +#pragma once + +#include <string> + +struct ProgramOptions { + std::string target_path_; + std::string grammar_path_; + std::string sc_app_path_; + std::string work_folder_; + std::string fitness_features_; + std::size_t population_size_; + std::size_t generations_; + std::size_t tournament_size_; + double fitness_threshold_; + double crossover_rate_; + double mutation_rate_; + bool reproduce_best_individual_; + std::size_t analysis_window_size_; + std::size_t analysis_hop_size_; + std::size_t maximum_tree_depth_; + bool keep_temp_folders_; + std::size_t core_limit_; +}; + +void ParseCommandLine(int argument_count, + const char* arguments[], + ProgramOptions* options);
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/src/range.hpp Thu Aug 25 11:05:55 2011 +0100 @@ -0,0 +1,201 @@ +// Copyright 2011, Ian Hobson. +// +// This file is part of gpsynth. +// +// gpsynth is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// gpsynth is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with gpsynth in the file COPYING. +// If not, see http://www.gnu.org/licenses/. + +#pragma once + +#include <ostream> + +namespace stdx { + +// like std::pair, designed for minimum/maximum +template<typename T> +struct Range { + T minimum_; + T maximum_; + + Range() + : minimum_(T()), + maximum_(T()) + {} + + Range(T minimum, T maximum) + : minimum_(minimum), + maximum_(maximum) + {} + + template<typename T2> + Range(const Range<T2>& other) + : minimum_(other.minimum_), + maximum_(other.maximum_) + {} + + // getters / setters + T Minimum() const { return minimum_; } + T Maximum() const { return maximum_; } + + void SetMinimum(const T& value) { minimum_ = value; } + void SetMaximum(const T& value) { maximum_ = value; } + + void SetRange(const T& minimum, const T& maximum) { + minimum_ = minimum; + maximum_ = maximum; + } + + T Size() const { return maximum_ - minimum_; } + + // Assignment operators + template<typename T2> + void operator=(const Range<T2>& other) { + minimum_ = other.minumum_; + maximum_ = other.maximum_; + } + + template<typename T2> + Range<T>& operator+=(const Range<T2>& other) { + minimum_ += other.minimum_; + maximum_ += other.maximum_; + return *this; + } + template<typename T2> + Range<T>& operator+=(const T2& value) { + minimum_ += value; + maximum_ += value; + return *this; + } + + template<typename T2> + Range<T>& operator-=(const Range<T2>& other) { + minimum_ -= other.minimum_; + maximum_ -= other.maximum_; + return *this; + } + template<typename T2> + Range<T>& operator-=(const T2& value) { + minimum_ -= value; + maximum_ -= value; + return *this; + } + + template<typename T2> + Range<T>& operator*=(const T2& value) { + minimum_ *= value; + maximum_ *= value; + return *this; + } + template<typename T2> + Range<T>& operator/=(const T2& value) { + minimum_ /= value; + maximum_ /= value; + return *this; + } + + + // Arithmetic + template<typename T2> + Range<T> operator+(const T2& value) { + return Range<T>(minimum_ + value, maximum_ + value); + } + template<typename T2> + Range<T> operator+(const Range<T2>& other) { + return Range<T>(minimum_ + other.minimum_, maximum_ + other.maximum_); + } + + template<typename T2> + Range<T> operator-(const T2& value) { + return Range<T>(minimum_ - value, maximum_ - value); + } + template<typename T2> + Range<T> operator-(const Range<T2>& other) { + return Range<T>(minimum_ - other.minimum_, maximum_ - other.maximum_); + } + + template<typename T2> + Range<T> operator*(const T2& value) { + return Range<T>(minimum_ * value, maximum_ * value); + } + template<typename T2> + Range<T> operator/(const T2& value) { + return Range<T>(minimum_ / value, maximum_ / value); + } + + // Increments/Decrements + Range<T>& operator++() { + ++minimum_; + ++maximum_; + return *this; + } + Range<T>& operator--() { + --minimum_; + --maximum_; + return *this; + } + + Range<T> operator++(int) { + Range<T> temp(*this); + ++(*this); + return temp; + } + Range<T> operator--(int) { + Range<T> temp(*this); + --(*this); + return temp; + } + +}; + +// Comparison operators +template<typename T> +bool operator==(const Range<T>& x, const Range<T>& y) { + return (x.minimum_ == y.minimum_) && (x.maximum_ == y.maximum_); +} + +template<typename T> +bool operator<(const Range<T>& x, const Range<T>& y) { + return (x.minimum_ < y.minimum_) + || (!(y.minimum_ < x.minimum_) && (x.maximum_ < y.maximum_)); +} + +template<typename T> +bool operator!=(const Range<T>& x, const Range<T>& y) { + return !(x == y); +} + +template<typename T> +bool operator>(const Range<T>& x, const Range<T>& y) { + return y < x; +} + +template<typename T> +bool operator<=(const Range<T>& x, const Range<T>& y) { + return !(y < x); +} + +template<typename T> +bool operator>=(const Range<T>& x, const Range<T>& y) { + return !(x < y); +} + +// Output +template<typename T> +std::ostream& operator<<(std::ostream& out, const Range<T>& range) { + out << '[' << range.minimum_ << ',' << range.maximum_ << ']'; + return out; +} + +} // stdx namespace +
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/src/sc_converter.cpp Thu Aug 25 11:05:55 2011 +0100 @@ -0,0 +1,449 @@ +// Copyright 2011, Ian Hobson. +// +// This file is part of gpsynth. +// +// gpsynth is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// gpsynth is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with gpsynth in the file COPYING. +// If not, see http://www.gnu.org/licenses/. + +#include "sc_converter.hpp" + +#include "boost_ex.hpp" +#include "logger.hpp" + +#include "boost/graph/graphviz.hpp" +#include "boost/graph/topological_sort.hpp" + +#include <iomanip> +#include <fstream> + + +namespace { + +const std::string g_VariablePrefix("v"); +const std::string g_ArgumentPrefix("arg_"); +const int g_label_decimal_places = 2; + +} // namespace + + +namespace sc { + +// picks the lowest available variable for a command, or creates a new one +int Converter::AssignVariable(sg::Vertex vertex) { + int variable; + if (available_variables_.size() > 0) { + std::set<int>::iterator lowest_variable = available_variables_.begin(); + variable = *(lowest_variable); + available_variables_.erase(lowest_variable); + } else { + variable = variables_created_++; + } + // store the command's variable + variable_map_[vertex] = variable; + return variable; +} + +// retrieves the assigned variable for a specific command vertex +int Converter::GetVariable(sg::Vertex vertex) { + int variable = variable_map_[vertex]; + variable_map_.erase(vertex); + available_variables_.insert(variable); + return variable; +} + +void Converter::SynthDefSpecial(sg::Vertex vertex, + const sg::Node& node, + const sg::Graph& graph) { + if ((node.id_ == sc::Command::kMixer) + || (node.id_ == sc::Command::kMultiplier)) { + // e.g. v0 + v1 + v2 + command_script_ << '\t'; + // is this the root mixer? + bool root_command = (boost::out_degree(vertex, graph) == 0); + if (root_command) { + command_script_ << "Out.ar([0, 1], "; + } else { + command_script_ << g_VariablePrefix << AssignVariable(vertex) << " = "; + } + // clip output + command_script_ << "Clip."; + if (node.rate_ == sg::kAudioRate) { + command_script_ << "ar("; + } else { + command_script_ << "kr("; + } + // process command parameter, based on vertex inputs + sg::Graph::in_edge_iterator in_edge; + sg::Graph::in_edge_iterator in_edge_end; + bool first_argument = true; + bool parentheses = (boost::in_degree(vertex, graph) > 1); + // get separator for this command + std::string separator; + if (node.id_ == sc::Command::kMixer) { + separator = " + "; + } else { + separator = " * "; + } + for (boost::tie(in_edge, in_edge_end) = boost::in_edges(vertex, graph); + in_edge != in_edge_end; + ++in_edge) { + const sg::Connection& connection = graph[*in_edge]; + if (!first_argument) { + command_script_ << separator; + } else { + first_argument = false; + } + const sg::Vertex& source_vertex = boost::source(*in_edge, graph); + const sg::Node& source = graph[source_vertex]; + sg::NodeType source_type = source.type_; + if (source_type == sg::kConstant) { + // calculate weighted constant value + double value = source.constant_value_; + command_script_ << (value * connection.weight_ + connection.offset_); + } else { + if (parentheses) { + command_script_ << '('; + } + if (source_type == sg::kParameter) { + command_script_ << g_ArgumentPrefix << source.id_; + } else if (source_type == sg::kCommand) { + command_script_ << g_VariablePrefix << GetVariable(source_vertex); + } else if (source_type == sg::kSpecial) { + command_script_ << g_VariablePrefix << GetVariable(source_vertex); + } + if (connection.weight_ != 1) { + command_script_ << " * " << connection.weight_; + } + if (connection.offset_ != 0) { + command_script_ << " + " << connection.offset_; + } + if (parentheses) { + command_script_ << ')'; + } + } + } + // end of Clip command + if (node.rate_ == sg::kAudioRate) { + command_script_ << ", -1, 1)"; + } else { + command_script_ << ", 0, 1)"; + } + // end of Out.ar command for root mixer + if (root_command) { + command_script_ << ')'; + } + command_script_ << ";\n"; + } +} + +void Converter::SynthDefCommand(sg::Vertex vertex, + const sg::Node& node, + const sg::Graph& graph) { + const sc::Command& command = commands_[node.id_]; + command_script_ << '\t'; + command_script_ << g_VariablePrefix << AssignVariable(vertex) << " = "; + command_script_ << command.Name() << '.'; + // command type + if (node.rate_ == sg::kAudioRate) { + command_script_ << "ar("; + } else { + command_script_ << "kr("; + } + // command inputs + sg::Graph::in_edge_iterator in_edge; + sg::Graph::in_edge_iterator in_edge_end; + bool first_argument = true; + for (boost::tie(in_edge, in_edge_end) = boost::in_edges(vertex, graph); + in_edge != in_edge_end; + ++in_edge) { + const sg::Connection& connection = graph[*in_edge]; + const sc::Argument& argument = command.GetArgument(connection.input_); + if (!first_argument) { + command_script_ << ", "; + } else { + first_argument = false; + } + // argument value + command_script_ << argument.name_ << ": "; + const sg::Vertex& source_vertex = boost::source(*in_edge, graph); + const sg::Node& source = graph[source_vertex]; + sg::NodeType source_type = source.type_; + if (source_type == sg::kConstant) { + // calculate weighted constant value + double value = source.constant_value_; + command_script_ << (value * connection.weight_ + connection.offset_); + } else { + if (source_type == sg::kParameter) { + command_script_ << g_ArgumentPrefix << source.id_; + } else if (source_type == sg::kCommand) { + command_script_ << g_VariablePrefix << GetVariable(source_vertex); + } else if (source_type == sg::kSpecial) { + command_script_ << g_VariablePrefix << GetVariable(source_vertex); + } + if (connection.weight_ != 1) { + command_script_ << " * " << connection.weight_; + } + if (connection.offset_ != 0) { + command_script_ << " + " << connection.offset_; + } + } + } + // write preset constant arguments + foreach (const sc::Argument& argument, command.Arguments()) { + if (argument.scaling_mode_ == sc::Argument::kConstant) { + if (!first_argument) { + command_script_ << ", "; + } + first_argument = false; + command_script_ << argument.name_ << ": " << argument.constant_value_; + } + } + //command end + command_script_ << ");\n"; +} + +// convert a given vertex +void Converter::SynthDefVertex(sg::Vertex vertex, const sg::Graph& graph) { + const sg::Node& node = graph[vertex]; + sg::NodeType type = node.type_; + if (type == sg::kSpecial) { + SynthDefSpecial(vertex, node, graph); + } + if (type == sg::kCommand) { + SynthDefCommand(vertex, node, graph); + } +} + +// prepares a synthdef from a synth graph +std::string Converter::ToSynthDef(const sg::Graph& graph, + const std::string& synth_name, + bool convert_for_realtime, + const std::string& synthdef_path) { + command_script_.str(""); + variable_map_.clear(); + variables_created_ = 0; + available_variables_.clear(); + + // preamble + std::stringstream synth_def; + synth_def << "SynthDef(\"" << synth_name << "\", {\n"; + // arguments + std::size_t parameters = graph[boost::graph_bundle].parameters_.size(); + if (parameters > 0) { + synth_def << "\targ "; + for (std::size_t i = 1; i <= parameters; i++) { + synth_def << g_ArgumentPrefix << i; + if (i < parameters) { + synth_def << ", "; + } + } + synth_def << ";\n"; + } + // commands + std::vector<sg::Vertex> vertices; + vertices.reserve(boost::num_vertices(graph)); + // perform topological sort on graph to find dependency order + boost::topological_sort(graph, std::back_inserter(vertices)); + // traverse commands in reversed topological order + std::map<sg::Vertex, int> variable_map; + foreach_reverse (sg::Vertex& vertex, vertices) { + SynthDefVertex(vertex, graph); + } + // declare variables required by the commands + if (variables_created_ > 0) { + synth_def << "\tvar "; + for (int i = 0; i < variables_created_; i++) { + synth_def << g_VariablePrefix << i; + if (i < variables_created_ - 1) { + synth_def << ", "; + } + } + synth_def << ";\n"; + } + // store the commands in the synthdef + synth_def << command_script_.str(); + // ending + if (convert_for_realtime) { + synth_def << "}).add;\n"; + } else { + synth_def << "}).writeDefFile(\"" << synthdef_path << "\");\n"; + } + return synth_def.str(); +} + +std::string Converter::ToScore(const sg::Graph& graph, + const std::string& synth_name, + const std::string& score_variable, + bool convert_for_realtime, + const std::string& synthdef_path, + double render_length) { + std::stringstream score; + score << score_variable << " = [\n"; + if (!convert_for_realtime) { + // load the synthdef + score << "[0.0, [\\d_load, \"" + << synthdef_path << '/' << synth_name << ".scsyndef\"]],\n"; + } + // start the synth + score << "[0.0, [\\s_new, \\" + synth_name + ", 1000, 0, 0"; + // initial parameter values + const sg::GraphProperties& graph_properties = graph[boost::graph_bundle]; + const std::vector<double>& parameters = graph_properties.parameters_; + for (int i = 0; i < parameters.size(); i++) { + score << ", \\" << g_ArgumentPrefix << (i + 1) << ", " << parameters[i]; + } + // end of \s_new command + score << "]],\n"; + if (!convert_for_realtime) { + // end of score, stop sound + score << "[" << render_length << ", [\\c_set, 0, 0]]\n"; + } + // score end + score << "];\n"; + return score.str(); +} + +void Converter::Export(const sg::Graph& graph, + const std::string& export_folder, + const std::string& export_name) { + std::stringstream export_path; + export_path << export_folder << '/' << export_name << ".sc"; + std::ofstream export_file(export_path.str().c_str()); + export_file << ToSynthDef(graph, export_name) + << ToScore(graph, export_name, "x"); + export_file << "Score.play(x);"; +} + + +#pragma mark - dot converter + +// formats a command node for dot +class CommandWriter { + const sg::Graph& graph_; + const std::vector<sc::Command>& commands_; + +public: + CommandWriter(const sg::Graph& graph, + const std::vector<sc::Command>& commands) + : graph_(graph), + commands_(commands) + {} + + void operator()(std::ostream& out, const sg::Vertex& vertex) const { + const sg::Node& command_info = graph_[vertex]; + sg::NodeType type = command_info.type_; + out << " [label=\""; + if (type == sg::kConstant) { + // calculate weighted constant value + const sg::Edge& out_edge = *(boost::out_edges(vertex, graph_).first); + const sg::Connection& connection = graph_[out_edge]; + double value = command_info.constant_value_; + value = value * connection.weight_ + connection.offset_; + out << std::setprecision(2) << std::fixed << value + << "\", shape=diamond"; + } else if (type == sg::kSpecial) { + if (command_info.id_ == sc::Command::kMixer) { + // check if this is the root mixer + if (boost::out_degree(vertex, graph_) > 0) { + out << "+\""; + if (command_info.rate_ == sg::kControlRate) { + out << ", shape=box"; + } + } else { + out << "Output\""; + } + } else if (command_info.id_ == sc::Command::kMultiplier) { + out << "*\""; + if (command_info.rate_ == sg::kControlRate) { + out << ", shape=box"; + } + } + } else if (type == sg::kCommand) { + out << commands_[command_info.id_].Name() << "\""; + if (command_info.rate_ == sg::kControlRate) { + out << ", shape=box"; + } + } else if (type == sg::kParameter) { + out << g_ArgumentPrefix << command_info.id_ + << "\", shape=invhouse"; + } + // end + out << "]"; + } +}; + +// formats a connection for dot +class ConnectionWriter { + const sg::Graph& graph_; + const std::vector<sc::Command>& commands_; + +public: + ConnectionWriter(const sg::Graph& graph, + const std::vector<sc::Command>& commands) + : graph_(graph), + commands_(commands) + {} + + void operator()(std::ostream& out, const sg::Edge& edge) const { + const sg::Connection& connection = graph_[edge]; + out << "[label=\""; + const sg::Node& target = graph_[boost::target(edge, graph_)]; + if (target.type_ == sg::kCommand) { + const sc::Command& command = commands_[target.id_]; + const sc::Argument& argument = command.GetArgument(connection.input_); + out << " [" << argument.name_ << ']'; + } + if (connection.IsActive()) { + // connection weighting + // skip constants as they have weighting applied to label + const sg::Node& source = graph_[boost::source(edge, graph_)]; + if (source.type_ != sg::kConstant) { + out << " ("; + out << std::fixed << std::setprecision(g_label_decimal_places); + if (connection.offset_ != 0) { + out << connection.offset_ + << " - " + << (connection.offset_ + connection.weight_); + } else { + out << "* " << connection.weight_; + } + out << ")"; + } + } + out << "\"]"; + } +}; + +class GraphPropertiesWriter { + const sg::Graph& graph_; +public: + GraphPropertiesWriter(const sg::Graph& graph) : graph_(graph) {} + void operator()(std::ostream& out) const { + out << "edge[fontsize=10]\n"; + } +}; + +std::string Converter::ToDOT(const sg::Graph& graph) { + std::stringstream buffer; + // prepare the writer classes + CommandWriter command_writer(graph, commands_); + ConnectionWriter connection_writer(graph, commands_); + GraphPropertiesWriter graph_writer(graph); + // write the graph to the buffer + boost::write_graphviz(buffer, graph, + command_writer, connection_writer, graph_writer); + return buffer.str(); +} + +} // sc namespace
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/src/sc_converter.hpp Thu Aug 25 11:05:55 2011 +0100 @@ -0,0 +1,80 @@ +// Copyright 2011, Ian Hobson. +// +// This file is part of gpsynth. +// +// gpsynth is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// gpsynth is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with gpsynth in the file COPYING. +// If not, see http://www.gnu.org/licenses/. + +// Converter class for SuperCollider, handles converting to synthdefs and dot +// graphs + +#pragma once + +#include "converter.hpp" +#include "sc_grammar.hpp" +#include "synth_graph.hpp" + +#include <map> +#include <set> +#include <sstream> +#include <vector> + +namespace sc { + +class Converter : public ConverterInterface { + const std::vector<sc::Command>& commands_; + std::map<sg::Vertex, int> variable_map_; + std::set<int> available_variables_; + int variables_created_; + std::stringstream command_script_; + +public: + Converter(const sc::Grammar& grammar) : commands_(grammar.Commands()) {} + + // converts a graph to graphviz DOT format + std::string ToDOT(const sg::Graph& graph); + + // generates a supercollider script intended for realtime use + void Export(const sg::Graph& graph, + const std::string& export_folder, + const std::string& export_name); + + // converts a graph to a supercollider synthdef + std::string ToSynthDef(const sg::Graph& graph, + const std::string& synth_name, + bool prepare_for_render = true, + const std::string& synthdef_path = ""); + + // generates a supercollider score for the graph + std::string ToScore(const sg::Graph& graph, + const std::string& synth_name, + const std::string& score_variable, + bool prepare_for_render = true, + const std::string& synthdef_path = "", + double render_length = 0); + +private: + void SynthDefVertex(sg::Vertex vertex, const sg::Graph& graph); + void SynthDefCommand(sg::Vertex vertex, + const sg::Node& node, + const sg::Graph& graph); + void SynthDefSpecial(sg::Vertex vertex, + const sg::Node& node, + const sg::Graph& graph); + + int AssignVariable(sg::Vertex vertex); + int GetVariable(sg::Vertex vertex); +}; + +} // sc namespace
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/src/sc_default_grammar.cpp Thu Aug 25 11:05:55 2011 +0100 @@ -0,0 +1,324 @@ +// Copyright 2011, Ian Hobson. +// +// This file is part of gpsynth. +// +// gpsynth is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// gpsynth is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with gpsynth in the file COPYING. +// If not, see http://www.gnu.org/licenses/. + +// Contents of grammar/sc.json + +#include "sc_default_grammar.hpp" + +namespace sc { + +const char* g_default_grammar = + "{\n" + " \"sclang-version\": \"3\",\n" + " \"name\": \"sc3\",\n" + " \"arg-types\": [\n" + " {\n" + " \"type\": \"frequency\",\n" + " \"range\": [20, 20000],\n" + " \"range_control\": [0, 100],\n" + " \"scaling\": \"log\"\n" + " },\n" + " {\n" + " \"type\": \"radians\",\n" + " \"range\": [0, 6.2831853072]\n" + " },\n" + " {\n" + " \"type\": \"audio\",\n" + " \"range\": [-1, 1]\n" + " },\n" + " {\n" + " \"type\": \"time\",\n" + " \"range\": [0, 10],\n" + " \"scaling\": \"log\"\n" + " },\n" + " {\n" + " \"type\": \"trigger\",\n" + " \"range\": [-1, 1],\n" + " \"fixed_range\": true\n" + " }\n" + " ],\n" + "\n" + " \"sources\": [\n" + " // oscillators\n" + " {\n" + " \"name\": \"SinOsc\",\n" + " \"args\": [\n" + " { \"name\": \"freq\", \"type\": \"frequency\" },\n" + " { \"name\": \"phase\", \"type\": \"radians\" }\n" + " ]\n" + " },\n" + " {\n" + " \"name\": \"Saw\",\n" + " \"args\": { \"name\": \"freq\", \"type\": \"frequency\" }\n" + " },\n" + " {\n" + " \"name\": \"SyncSaw\",\n" + " \"args\": [\n" + " { \"name\": \"syncFreq\", \"type\": \"frequency\" },\n" + " { \"name\": \"sawFreq\", \"type\": \"frequency\" }\n" + " ]\n" + " },\n" + " {\n" + " \"name\": \"VarSaw\",\n" + " \"args\": [ { \"name\": \"freq\", \"type\": \"frequency\" }, \"iphase\", \"width\" ]\n" + " },\n" + " {\n" + " \"name\": \"Pulse\",\n" + " \"args\": [ { \"name\": \"freq\", \"type\": \"frequency\" }, \"width\" ]\n" + " },\n" + " {\n" + " \"name\": \"Impulse\",\n" + " \"args\": [ { \"name\": \"freq\", \"type\": \"frequency\" }, \"phase\" ]\n" + " },\n" + " {\n" + " \"name\": \"Blip\",\n" + " \"args\": [\n" + " { \"name\": \"freq\", \"type\": \"frequency\" },\n" + " { \"name\": \"numharm\", \"range\": [1, 200] }\n" + " ]\n" + " },\n" + " {\n" + " \"name\": \"SinOscFB\",\n" + " \"args\": [\n" + " { \"name\": \"freq\", \"type\": \"frequency\" },\n" + " { \"name\": \"feedback\", \"type\": \"radians\" }\n" + " ]\n" + " },\n" + " {\n" + " \"name\": \"PMOsc\",\n" + " \"args\": [\n" + " { \"name\": \"carfreq\", \"type\": \"frequency\" },\n" + " { \"name\": \"modfreq\", \"type\": \"frequency\" },\n" + " { \"name\": \"pmindex\", \"type\": \"radians\" },\n" + " { \"name\": \"modphase\", \"type\": \"radians\" }\n" + " ]\n" + " },\n" + " {\n" + " \"name\": \"FBSineC\",\n" + " \"output\": \"audio\",\n" + " \"args\": [\n" + " { \"name\": \"freq\", \"type\": \"frequency\" },\n" + " { \"name\": \"im\", \"range\": [1, 64] },\n" + " { \"name\": \"fb\", \"range\": [0, 0.5] },\n" + " { \"name\": \"a\", \"range\": [1, 1.2] },\n" + " { \"name\": \"c\", \"range\": [0.1, 0.9] }\n" + " ]\n" + " },\n" + " {\n" + " \"name\": \"FBSineN\",\n" + " \"output\": \"audio\",\n" + " \"args\": [\n" + " { \"name\": \"freq\", \"type\": \"frequency\" },\n" + " { \"name\": \"im\", \"range\": [1, 64] },\n" + " { \"name\": \"fb\", \"range\": [0, 0.5] },\n" + " { \"name\": \"a\", \"range\": [1, 1.2] },\n" + " { \"name\": \"c\", \"range\": [0.1, 0.9] }\n" + " ]\n" + " },\n" + " {\n" + " \"name\": \"Logistic\",\n" + " \"args\": [\n" + " { \"name\": \"chaosParam\", \"range\": [3, 3.999] },\n" + " { \"name\": \"freq\", \"type\": \"frequency\" }\n" + " ]\n" + " },\n" + " // Noise\n" + " {\n" + " \"name\": \"LFNoise0\",\n" + " \"args\": { \"name\": \"freq\", \"type\": \"frequency\" }\n" + " },\n" + " \"WhiteNoise\",\n" + " \"BrownNoise\",\n" + " \"PinkNoise\",\n" + " {\n" + " \"name\": \"Crackle\",\n" + " \"args\": { \"name\": \"chaosParam\", \"range\": [1, 2] }\n" + " },\n" + " // Lines/Envelopes\n" + " {\n" + " \"name\": \"Line\",\n" + " \"output\": \"control\",\n" + " \"args\": { \"name\": \"dur\", \"type\": \"time\" }\n" + " },\n" + " {\n" + " \"name\": \"XLine\",\n" + " \"output\": \"control\",\n" + " \"args\": [ \n" + " { \"name\": \"dur\", \"type\": \"time\" },\n" + " { \"name\": \"add\", \"constant\": -1 }\n" + " ]\n" + " },\n" + " {\n" + " \"name\": \"Linen\",\n" + " \"output\": \"control\",\n" + " \"args\": [\n" + " \"gate\",\n" + " { \"name\": \"attackTime\", \"type\": \"time\" },\n" + " \"susLevel\", \n" + " { \"name\": \"releaseTime\", \"type\": \"time\" }\n" + " ]\n" + " }\n" + " ],\n" + "\n" + " \"modifiers\": [\n" + " // Filters\n" + " {\n" + " \"name\": \"LPF\",\n" + " \"args\": { \"name\": \"freq\", \"type\": \"frequency\" }\n" + " },\n" + " {\n" + " \"name\": \"HPF\",\n" + " \"args\": { \"name\": \"freq\", \"type\": \"frequency\" }\n" + " },\n" + " {\n" + " \"name\": \"BPF\",\n" + " \"args\": [ { \"name\": \"freq\", \"type\": \"frequency\" }, \"rq\" ]\n" + " },\n" + " {\n" + " \"name\": \"BRF\",\n" + " \"args\": [ { \"name\": \"freq\", \"type\": \"frequency\" }, \"rq\" ]\n" + " },\n" + " {\n" + " \"name\": \"RLPF\",\n" + " \"args\": [ { \"name\": \"freq\", \"type\": \"frequency\" }, \"rq\" ]\n" + " },\n" + " {\n" + " \"name\": \"RHPF\",\n" + " \"args\": [ { \"name\": \"freq\", \"type\": \"frequency\" }, \"rq\" ]\n" + " },\n" + " {\n" + " \"name\": \"Resonz\",\n" + " \"args\": [ { \"name\": \"freq\", \"type\": \"frequency\" }, \"bwr\" ]\n" + " },\n" + " {\n" + " \"name\": \"Ringz\",\n" + " \"args\": [ \n" + " { \"name\": \"freq\", \"type\": \"frequency\" },\n" + " { \"name\": \"decaytime\", \"type\": \"time\" }\n" + " ]\n" + " },\n" + " {\n" + " \"name\": \"Formlet\",\n" + " \"args\": [\n" + " { \"name\": \"freq\", \"type\": \"frequency\" },\n" + " { \"name\": \"attacktime\", \"type\": \"time\" },\n" + " { \"name\": \"decaytime\", \"type\": \"time\" }\n" + " ]\n" + " },\n" + " {\n" + " \"name\": \"MoogFF\",\n" + " \"args\": [ \n" + " { \"name\": \"freq\", \"type\": \"frequency\" },\n" + " { \"name\": \"gain\", \"range\": [0, 4] }\n" + " ]\n" + " },\n" + " {\n" + " \"name\": \"FreeVerb\",\n" + " \"output\": \"audio\",\n" + " \"args\": [ \"mix\", \"room\", \"damp\" ]\n" + " },\n" + " // Dynamics\n" + " {\n" + " \"name\": \"Limiter\",\n" + " \"output\": \"audio\",\n" + " \"args\": \"level\"\n" + " },\n" + " {\n" + " \"name\": \"Amplitude\",\n" + " \"output\": \"control\",\n" + " \"args\": [\n" + " { \"name\": \"attackTime\", \"type\": \"time\" },\n" + " { \"name\": \"releaseTime\", \"type\": \"time\" }\n" + " ]\n" + " },\n" + " {\n" + " \"name\": \"Normalizer\",\n" + " \"output\": \"audio\",\n" + " \"args\": [ \"level\", { \"name\": \"dur\", \"range\": [0.001, 0.5] } ]\n" + " },\n" + " {\n" + " \"name\": \"Compander\",\n" + " \"output\": \"audio\",\n" + " \"args\": [ \n" + " { \"name\": \"control\", \"type\": \"audio\" },\n" + " \"thresh\",\n" + " { \"name\": \"slopeBelow\", \"type\": \"time\" },\n" + " { \"name\": \"slopeAbove\", \"type\": \"time\" },\n" + " { \"name\": \"clampTime\", \"range\": [0.001, 0.2] },\n" + " { \"name\": \"relaxTime\", \"range\": [0.001, 0.2] }\n" + " ]\n" + " },\n" + " // shifters\n" + " {\n" + " \"name\": \"FreqShift\",\n" + " \"output\": \"audio\",\n" + " \"args\": [\n" + " { \"name\": \"freq\", \"range\": [-1000, 1000] },\n" + " { \"name\": \"phase\", \"type\": \"radians\" }\n" + " ]\n" + " },\n" + " {\n" + " \"name\": \"PitchShift\",\n" + " \"output\": \"audio\",\n" + " \"args\": [\n" + " { \"name\": \"pitchRatio\", \"range\": [0, 4] },\n" + " { \"name\": \"pitchDispersion\", \"range\": [0, 4] },\n" + " { \"name\": \"timeDispersion\", \"range\": [0, 0.2] }\n" + " ]\n" + " },\n" + " // delays\n" + " {\n" + " \"name\": \"DelayC\",\n" + " \"args\": [\n" + " { \"name\": \"maxdelaytime\", \"constant\": 1 },\n" + " { \"name\": \"delaytime\", \"range\": [0.001, 1] }\n" + " ]\n" + " },\n" + " {\n" + " \"name\": \"Pluck\",\n" + " \"output\": \"audio\",\n" + " \"args\": [\n" + " { \"name\": \"trig\", \"type\": \"trigger\" },\n" + " { \"name\": \"maxdelaytime\", \"constant\": 1 },\n" + " { \"name\": \"delaytime\" },\n" + " { \"name\": \"decaytime\", \"type\": \"time\" },\n" + " { \"name\": \"coef\", \"range\": [-0.9, 0.9] }\n" + " ]\n" + " },\n" + " {\n" + " \"name\": \"CombC\",\n" + " \"args\": [\n" + " { \"name\": \"maxdelaytime\", \"constant\": 1 },\n" + " { \"name\": \"delaytime\" },\n" + " { \"name\": \"decaytime\", \"range\": [-1, 1] }\n" + " ]\n" + " },\n" + " // misc\n" + " {\n" + " \"name\": \"Decay\",\n" + " \"args\": { \"name\": \"decayTime\", \"type\": \"time\" }\n" + " },\n" + " {\n" + " \"name\": \"Latch\",\n" + " \"args\": { \"name\": \"trig\", \"type\": \"trigger\" }\n" + " }\n" + " ] // end of modifiers\n" + "}\n" +; + +} // sc namespace
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/src/sc_default_grammar.hpp Thu Aug 25 11:05:55 2011 +0100 @@ -0,0 +1,25 @@ +// Copyright 2011, Ian Hobson. +// +// This file is part of gpsynth. +// +// gpsynth is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// gpsynth is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with gpsynth in the file COPYING. +// If not, see http://www.gnu.org/licenses/. + +// Default SuperCollider grammar for GPSynth + +#pragma once + +namespace sc { + extern const char* g_default_grammar; +}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/src/sc_evaluator.cpp Thu Aug 25 11:05:55 2011 +0100 @@ -0,0 +1,408 @@ +// Copyright 2011, Ian Hobson. +// +// This file is part of gpsynth. +// +// gpsynth is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// gpsynth is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with gpsynth in the file COPYING. +// If not, see http://www.gnu.org/licenses/. + +#include "sc_evaluator.hpp" + +#include "boost_ex.hpp" +#include "logger.hpp" +#include "sc_converter.hpp" +#include "statistics.hpp" + +#include "boost/filesystem.hpp" +#include "boost/format.hpp" +#include "boost/lexical_cast.hpp" +#include "boost/process.hpp" +#include "boost/thread.hpp" +#include "boost/math/special_functions/fpclassify.hpp" + +#include <algorithm> +#include <cmath> +#include <functional> +#include <numeric> + +namespace bp = boost::process; + +namespace sc { + + const int g_max_file_wait_time_ms = 10000; + const int g_file_wait_increment_ms = 500; + const int g_sclang_timeout_s = 10; + const double g_fitness_failure_value = 100; + const std::string g_score_variable_name = "x"; + +// Worker class, runs in own thread +class EvaluatorWorker { + sc::Converter converter_; + int thread_id_; + bool quit_threads_; + Evaluator* parent_; + dsp::FileComparer file_comparer_; + // work thread communicates with parent, handles work queue + boost::thread work_thread_; + // render thread launches child processes + boost::thread render_thread_; + boost::mutex render_mutex_; + // render duration in seconds + double render_length_; + std::string render_command_path_; + std::string render_osc_path_; + std::string render_output_path_; + // flag for render thread that a synth is ready for rendering + bool do_render_; + // condition to notify render thread that work is available + boost::condition_variable render_start_; + // condition to notify work thread that work has been done + boost::condition_variable render_finished_; + // stores handle to active child process + boost::shared_ptr<bp::child> render_process_; + +public: + EvaluatorWorker(Evaluator* parent, + int thread_id, + const dsp::FileComparer& target) + : parent_(parent), + thread_id_(thread_id), + converter_(parent->grammar_), + quit_threads_(false), + file_comparer_(target), + render_length_(file_comparer_.TargetDuration()), + do_render_(false) + { + work_thread_ = boost::thread(&EvaluatorWorker::DoWork, this); + render_thread_ = boost::thread(&EvaluatorWorker::DoSCRender, this); + } + + ~EvaluatorWorker() { + // tell threads to quit + quit_threads_ = true; + // work thread + try { + boost::system_time timeout = boost::get_system_time(); + timeout += boost::posix_time::seconds(g_sclang_timeout_s); + parent_->queue_condition_.notify_all(); + work_thread_.timed_join(timeout); + // render thread + timeout = boost::get_system_time(); + timeout += boost::posix_time::seconds(g_sclang_timeout_s); + render_start_.notify_one(); + render_thread_.timed_join(timeout); + } + catch (const std::exception& e) { + std::stringstream message; + message << "~sc::EvaluatorWorker - Exception: " << e.what() << '\n'; + WorkerLog(message.str()); + } + } + +private: + + void DoWork() { + while (!quit_threads_) { + // wait for some work + { + boost::mutex::scoped_lock lock(parent_->work_mutex_); + while (parent_->work_queue_.empty() && !quit_threads_) { + parent_->queue_condition_.wait(lock); + } + } + // keep going until the work queue is empty + while (!quit_threads_) { + sg::Graph* graph = NULL; + { + // check there's still work to do + boost::mutex::scoped_lock lock(parent_->work_mutex_); + if (parent_->work_queue_.empty()) { + // wait for all threads to reach this point before continuing + lock.unlock(); + parent_->work_done_barrier_->wait(); + lock.lock(); + // notify main thread that work's finished + parent_->work_done_condition_.notify_one(); + break; + } + // get the next graph from the queue + graph = parent_->work_queue_.front(); + parent_->work_queue_.pop(); + } + // get the graph properties + sg::Graph& graph_ref = *graph; + sg::GraphProperties& graph_properties = graph_ref[boost::graph_bundle]; + double fitness; + try { + // rate the graph + fitness = RateGraph(graph_ref); + } catch (const std::exception& e) { + std::stringstream message; + message << "Exception: " << e.what() << '\n'; + WorkerLog(message.str()); + fitness = g_fitness_failure_value; + } + if (boost::math::isinf(fitness)) { + fitness = g_fitness_failure_value; + } else if (boost::math::isnan(fitness)) { + fitness = g_fitness_failure_value; + } + graph_properties.fitness_ = fitness; +// std::stringstream message; +// message << "Rated " << graph_properties.id_ << ":" << fitness << '\n'; +// WorkerLog(message.str()); + { + // decrement the graph queue count + boost::mutex::scoped_lock lock(parent_->work_mutex_); + parent_->graphs_to_rate_--; + parent_->work_done_condition_.notify_one(); + } + } + } + } + + void WorkerLog(const std::string& message) { + std::stringstream formatter; + formatter << "\n[" << thread_id_ << "] " << message; + logger::Log(formatter.str()); + } + + double RateGraph(sg::Graph& graph) { + int id = graph[boost::graph_bundle].id_; + std::stringstream name; + name << "gpsynth_" + << std::setw(parent_->synth_name_zero_pad_length_) << std::setfill('0') + << id; + std::string synth_name = name.str(); + // convert the graph to a synthdef and score + std::string synthdef = converter_.ToSynthDef(graph, + synth_name, + false, + parent_->synthdef_folder_); + std::string score = converter_.ToScore(graph, + synth_name, + g_score_variable_name, + false, // prepare for non-realtime + parent_->synthdef_folder_, + render_length_); + { + // prepare the synthdef and score for the render thread + boost::mutex::scoped_lock lock(render_mutex_); + render_command_path_ = parent_->sc_folder_ + synth_name + ".sc"; + render_osc_path_ = parent_->osc_folder_ + synth_name + ".osc"; + render_output_path_ = parent_->audio_folder_+ synth_name + ".aiff"; + // prepare render script for sclang + std::ofstream command_file(render_command_path_.c_str()); + // write the synthdef and the score + command_file << synthdef << '\n' << score; + // tell sclang to write the score as OSC data + command_file << "Score.write(" << g_score_variable_name << ", \"" + << render_osc_path_ << "\");\n"; + // tell sclang to quit at end of script + command_file << "\n0.exit;"; + // finished, close the file + command_file.close(); + // tell the render thread that work is available + do_render_ = true; + boost::system_time timeout = boost::get_system_time(); + timeout += boost::posix_time::seconds(g_sclang_timeout_s); + render_start_.notify_one(); + // wait for the render thread to complete the job + if (!render_finished_.timed_wait(lock, timeout)) { + // sclang or scsynth timed out, kill process and set fitness to failure + // WorkerLog("Render thread timed out.\n"); + try { + render_process_->terminate(); + } + catch (boost::system::system_error& e) { + // don't need to do anything here, process may already be terminated. + } + do_render_ = false; + // maximum error + return g_fitness_failure_value; + } + } + // check that a rendered file has been produced + if (!boost::filesystem::exists(render_output_path_)) { + // file missing, something's gone wrong.. + WorkerLog("Render output missing.\n"); + return g_fitness_failure_value; + } + // store render path with graph for future reference + graph[boost::graph_bundle].render_path_ = render_output_path_; + // compare the rendered file to the target + return CompareToTarget(render_output_path_); + } + + void DoSCRender() { + std::vector<std::string> args; + while (!quit_threads_) { + // wait for a command to render + { + boost::mutex::scoped_lock lock(render_mutex_); + do_render_ = false; + while (!do_render_ && !quit_threads_) { + render_start_.wait(lock); + } + if (quit_threads_) { + return; + } + } + // Call sclang to get OSC command for scsynth + args.clear(); + args.push_back("-d"); + args.push_back(parent_->sc_app_path_); + args.push_back(render_command_path_); + // replace process pipes to suppress output + bp::context closed_streams; + closed_streams.streams[bp::stdout_id] = bp::behavior::close(); + closed_streams.streams[bp::stderr_id] = bp::behavior::close(); + bp::child process = bp::create_child(parent_->sclang_path_, + args, + closed_streams); + // store process handle to allow work thread to kill process if it's hung + { + boost::mutex::scoped_lock lock(render_mutex_); + render_process_.reset(new bp::child(process)); + } + // wait for sclang to finish + process.wait(); + if (!do_render_) { + // if do_render_ is false, process was killed by work thread + continue; + } + // check that sclang generated an osc command + if (!boost::filesystem::exists(render_osc_path_)) { + // sclang failed, notify work thread and continue + boost::mutex::scoped_lock lock(render_mutex_); + render_finished_.notify_one(); + continue; + } + // render osc with scsynth + args.clear(); + args.push_back("-N"); + args.push_back(render_osc_path_); + args.push_back("_"); // no input file + args.push_back(render_output_path_); + args.push_back("44100"); + args.push_back("AIFF"); + args.push_back("int16"); + // output channels + args.push_back("-o"); + args.push_back("1"); + // don't publish to rendevous + args.push_back("-R"); + args.push_back("0"); + // don't load synthdefs + args.push_back("-D"); + args.push_back("0"); + // plugins path + args.push_back("-U"); + args.push_back(parent_->sc_plugins_path_); + // call scsynth + process = bp::create_child(parent_->scsynth_path_, args, closed_streams); + { + boost::mutex::scoped_lock lock(render_mutex_); + render_process_.reset(new bp::child(process)); + } + // wait for scsynth to finish rendering + process.wait(); + if (!do_render_) { + // process timed out, killed by work thread + continue; + } + // job done, notify work thread + { + boost::mutex::scoped_lock lock(render_mutex_); + render_finished_.notify_one(); + } + } + } + + double CompareToTarget(const std::string& file_path) { + return file_comparer_.CompareFile(file_path); + } +}; + + +//////////////////////////////////////////////////////////////////////////////// +//////////////////////////////////////////////////////////////////////////////// + +Evaluator::Evaluator(const sc::Grammar& grammar, + const std::string& sc_app_path, + const dsp::FileComparer& target, + int core_limit /* = 0 */) +: grammar_(grammar), + listener_(NULL), + sc_app_path_(sc_app_path), + sc_plugins_path_(sc_app_path + "/plugins"), + sclang_path_(sc_app_path + "/sclang"), + scsynth_path_(sc_app_path + "/scsynth") +{ + SetWorkFolder("/tmp/"); + int workers = boost::thread::hardware_concurrency(); + if (core_limit > 0 && core_limit < workers) { + workers = core_limit; + } + work_done_barrier_.reset(new boost::barrier(workers)); + for (int worker_id = 0; worker_id < workers; worker_id++) { + workers_.push_back(EvaluatorWorkerPtr(new EvaluatorWorker(this, + worker_id, + target))); + } +} + +Evaluator::~Evaluator() +{} + + +void Evaluator::RateGraphs(std::vector<sg::Graph>& graphs) { + // make sure necessary subfolders exist in work folder + audio_folder_ = work_folder_ + "/audio/"; + osc_folder_ = work_folder_ + "/osc/"; + sc_folder_ = work_folder_ + "/sc/"; + synthdef_folder_ = work_folder_ + "/synthdefs/"; + boost::filesystem::create_directories(audio_folder_); + boost::filesystem::create_directories(osc_folder_); + boost::filesystem::create_directories(sc_folder_); + boost::filesystem::create_directories(synthdef_folder_); + // find zero padding length for synth names + synth_name_zero_pad_length_ = std::log10(graphs.size()) + 1; + // store the graphs in the work queue + boost::mutex::scoped_lock lock(work_mutex_); + graphs_to_rate_ = static_cast<int>(graphs.size()); + for (int i = 0; i < graphs_to_rate_; i++) { + work_queue_.push(&graphs[i]); + } + // wait for graphs to be rated + std::size_t last_rated_count = 0; + while (graphs_to_rate_ > 0) { + queue_condition_.notify_all(); + work_done_condition_.wait(lock); + if (listener_ != NULL) { + std::size_t rated_count = graphs.size() - graphs_to_rate_; + if (rated_count != last_rated_count) { + last_rated_count = rated_count; + listener_->GraphRatedNotification(rated_count); + } + } + } + // remove OSC folder, no longer required + boost::filesystem::remove_all(osc_folder_); +} + +void Evaluator::SetWorkFolder(const std::string& path) { + work_folder_ = path; +} + +} // sc namespace
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/src/sc_evaluator.hpp Thu Aug 25 11:05:55 2011 +0100 @@ -0,0 +1,77 @@ +// Copyright 2011, Ian Hobson. +// +// This file is part of gpsynth. +// +// gpsynth is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// gpsynth is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with gpsynth in the file COPYING. +// If not, see http://www.gnu.org/licenses/. + +// Evaluator for SuperCollider synth graphs. Sends converted synthdefs to +// SuperCollider for rendering. + +#pragma once + +#include "evaluator.hpp" +#include "file_comparer.hpp" +#include "sc_grammar.hpp" + +#include "boost/shared_ptr.hpp" +#include "boost/thread/barrier.hpp" +#include "boost/thread/condition_variable.hpp" +#include "boost/thread/mutex.hpp" + +#include <queue> +#include <string> + +namespace sc { + +// forward declaration of worker +class EvaluatorWorker; +typedef boost::shared_ptr<EvaluatorWorker> EvaluatorWorkerPtr; + +class Evaluator : public EvaluatorInterface { + friend class EvaluatorWorker; + const sc::Grammar& grammar_; + const std::string sc_app_path_; + const std::string sc_plugins_path_; + const std::string sclang_path_; + const std::string scsynth_path_; + std::string work_folder_; + std::string osc_folder_; + std::string audio_folder_; + std::string sc_folder_; + std::string synthdef_folder_; + std::queue<sg::Graph*> work_queue_; + boost::mutex work_mutex_; + boost::condition_variable queue_condition_; + boost::condition_variable work_done_condition_; + boost::shared_ptr<boost::barrier> work_done_barrier_; + std::vector<EvaluatorWorkerPtr> workers_; + std::size_t graphs_to_rate_; + EvaluatorListenerInterface* listener_; + int synth_name_zero_pad_length_; + +public: + Evaluator(const sc::Grammar& grammar, + const std::string& sc_app_path, + const dsp::FileComparer& target, + int core_limit = 0); + ~Evaluator(); + void RateGraphs(std::vector<sg::Graph>& graphs); + void SetWorkFolder(const std::string& path); + void SetListener(EvaluatorListenerInterface* listener) { + listener_ = listener; + } +}; + +} // sc namespace
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/src/sc_grammar.cpp Thu Aug 25 11:05:55 2011 +0100 @@ -0,0 +1,904 @@ +// Copyright 2011, Ian Hobson. +// +// This file is part of gpsynth. +// +// gpsynth is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// gpsynth is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with gpsynth in the file COPYING. +// If not, see http://www.gnu.org/licenses/. + +#include "sc_grammar.hpp" + +#include "boost_ex.hpp" +#include "graph_helpers.hpp" +#include "logger.hpp" +#include "statistics.hpp" +#include "std_ex.hpp" + +#include "json/json.h" + +#include "boost/graph/random.hpp" +#include "boost/graph/topological_sort.hpp" + +#include <iostream> +#include <iterator> +#include <fstream> +#include <set> +#include <sstream> + +const int g_maximum_mixer_channels = 3; + +const double g_probability_command_node_command = 0.85; +const double g_probability_command_node_special = 0.15; + +const double g_probability_mod_node_command = 0.25; +const double g_probability_mod_node_special = 0.15; +const double g_probability_mod_node_constant = 0.4; + +const double g_probability_argument_affected = 1.0; + +const double g_probability_input_mutation = 1.0; +const double g_probability_connection_mutation = 1.0; + +const double g_minimum_connection_weight = 0.001; + +namespace { + + double MapCoefficientToRange(double c, const stdx::Range<double>& range) { + return range.minimum_ + (range.Size() * c); + } + + // Returns argument connection weight and offset, scaled to argument input + void GenerateWeightAndOffset(const sc::Argument& argument, + sg::CommandRate rate, + sg::Connection* connection) { + const stdx::Range<double>& scale_range = (rate == sg::kControlRate) + ? argument.range_control_ + : argument.range_; + if (argument.fixed_range_) { + connection->weight_ = scale_range.Size(); + connection->offset_ = scale_range.Minimum(); + } else if (argument.name_ == "in") { + // input connections don't have an offset + double weight = stdx::RandomCoefficient(); + if (rate == sg::kAudioRate) { + // square audio weightings + connection->weight_ = weight * weight; + } + } else { + // generate random weight and offset + double weight; + do { + weight = stdx::RandomCoefficient(); + } while (weight < g_minimum_connection_weight); + double offset = stdx::RandomRange(0.0, 1.0 - weight); + switch (argument.scaling_mode_) { + case sc::Argument::kScalingLinear: + connection->weight_ = MapCoefficientToRange(weight, scale_range); + connection->offset_ = MapCoefficientToRange(offset, scale_range); + break; + + case sc::Argument::kScalingLog: + connection->weight_ = MapCoefficientToRange(weight * weight * weight, + scale_range); + connection->offset_ = MapCoefficientToRange(offset * offset * offset, + scale_range); + break; + + case sc::Argument::kConstant: + throw std::runtime_error("ApplyRandomWeightAndOffset - preset argument provided"); + } + } + } + +sc::Argument ParseArgument(const Json::Value& json) { + sc::Argument arg; + arg.name_ = json.get("name", "").asString(); + if (json.isMember("constant")) { + arg.scaling_mode_ = sc::Argument::kConstant; + arg.constant_value_ = json["constant"].asDouble(); + return arg; + } + if (json.isMember("range")) { + const Json::Value range = json["range"]; + arg.range_.SetMinimum(range[0].asDouble()); + arg.range_.SetMaximum(range[1].asDouble()); + } + if (json.isMember("range_control")) { + const Json::Value range = json["range_control"]; + arg.range_control_.SetMinimum(range[0].asDouble()); + arg.range_control_.SetMaximum(range[1].asDouble()); + } else { + arg.range_control_ = arg.range_; + } + if (json.isMember("fixed_range")) { + arg.fixed_range_ = json["fixed_range"].asBool(); + } + std::string scaling = json.get("scaling", "linear").asString(); + if (scaling == "linear") { + arg.scaling_mode_ = sc::Argument::kScalingLinear; + } else if (scaling == "log") { + arg.scaling_mode_ = sc::Argument::kScalingLog; + } + return arg; +} + +sc::Command ParseCommand(const Json::Value& json, + const std::map<std::string, sc::Argument>& types, + sc::Command::CommandMode command_mode) { + if (json.type() == Json::stringValue) { + return sc::Command(json.asString(), command_mode); + } + // name + std::string name = json["name"].asString(); + // output type + sc::Command::OutputType output_type = sc::Command::kAll; + if (json.isMember("output")) { + std::string output = json["output"].asString(); + if (output == "control") { + output_type = sc::Command::kControl; + } else if (output == "audio") { + output_type = sc::Command::kAudio; + } + } + // arguments + std::vector<sc::Argument> args; + // [in] argument for modifiers + if (command_mode == sc::Command::kModifier) { + args.push_back(sc::Argument("in")); + } + Json::Value json_args = json["args"]; + Json::ValueType arg_type = json_args.type(); + if (arg_type == Json::stringValue) { + // single argument as string + args.push_back(sc::Argument(json_args.asString())); + } else if (arg_type == Json::objectValue) { + // single argument object + if (json_args.isMember("type")) { + args.push_back(stdx::GetFromMap(types, json_args["type"].asString())); + args.back().name_ = json_args["name"].asString(); + } else { + args.push_back(ParseArgument(json_args)); + } + } else { + // array of arguments + for (int i = 0; i < json_args.size(); i++) { + Json::Value arg = json_args[i]; + if (arg.type() == Json::stringValue) { + args.push_back(sc::Argument(arg.asString())); + } else { + if (arg.isMember("type")) { + args.push_back(stdx::GetFromMap(types, arg["type"].asString())); + args.back().name_ = arg["name"].asString(); + } else { + args.push_back(ParseArgument(arg)); + } + } + } + } + // success + return sc::Command(name, command_mode, output_type, args); +} + + +sc::Command ParseSource(const Json::Value& json, + const std::map<std::string, sc::Argument>& types) { + return ParseCommand(json, types, sc::Command::kSource); +} + +sc::Command ParseModifier(const Json::Value& json, + const std::map<std::string, sc::Argument>& types) { + return ParseCommand(json, types, sc::Command::kModifier); +} + +void MutateValue(double* value, bool* mutation_occurred, + double range_min = 0.0, double range_max = 1.0) { + // add gaussian noise to values, low variance + static bx::GaussianGenerator generator = bx::MakeGaussianGenerator(0, 0.125); + double new_value = stdx::Clamp(*value + generator(), range_min, range_max); + if (*value != new_value) { + *value = new_value; + *mutation_occurred = true; + } +} + +} // namespace + + + +namespace sc { + +Grammar::Grammar(const std::string& json_data) +: max_depth_(3) +{ + ParseJSON(json_data); +} + +void Grammar::ParseJSON(const std::string& json_data) { + commands_.clear(); + Json::Reader reader; + Json::Value root; + if (!reader.parse(json_data, root)) { + std::stringstream message; + message << "Grammar::Parse: Failed to parse data\n" + << reader.getFormattedErrorMessages(); + throw std::runtime_error(message.str()); + } + try { + // argument types + // stores predefined argument types while parsing grammar + std::map<std::string, Argument> argument_types; + const Json::Value& args = root["arg-types"]; + for (int i = 0; i < args.size(); i++) { + const Json::Value& arg = args[i]; + argument_types[arg["type"].asString()] = ParseArgument(arg); + } + // sound sources + const Json::Value& sources = root["sources"]; + for (int i = 0; i < sources.size(); i++) { + commands_.push_back(ParseSource(sources[i], argument_types)); + } + // modifiers + const Json::Value& modifiers = root["modifiers"]; + for (int i = 0; i < modifiers.size(); i++) { + commands_.push_back(ParseModifier(modifiers[i], argument_types)); + } + } catch (const std::exception& e) { + std::stringstream message; + message << "Grammar::Parse: " << e.what(); + throw std::runtime_error(message.str()); + } +} + +int Grammar::RandomCommand(sg::CommandRate rate, + int tree_depth, + bool must_be_modifier /* = false */) const { + Command::OutputType output; + Command::CommandMode mode; + CommandID command = -1; + CommandID number_of_commands = commands_.size(); + // if we're at or one away from maximum depth then the command needs to + // be a source, headroom of 1 for some parameter control + bool must_be_source = (tree_depth >= (max_depth_ - 1)); + do { + command = stdx::Random(number_of_commands); + output = commands_[command].Output(); + if (rate == sg::kAudioRate) { + do { + command = stdx::Random(number_of_commands); + output = commands_[command].Output(); + } while (!(output == Command::kAll || output == Command::kAudio)); + } else { + do { + command = stdx::Random(number_of_commands); + output = commands_[command].Output(); + } while (!(output == Command::kAll || output == Command::kControl)); + } + mode = commands_[command].Mode(); + } while ((must_be_source && (mode != Command::kSource)) + || (must_be_modifier && (mode != Command::kModifier))); + return static_cast<int>(command); +} + +sg::Vertex Grammar::CreateCommand(sg::Graph &graph, + sg::CommandRate rate, + int depth, + bool must_be_modifier /* = false */, + bool make_modifier_input /* = true */) const { + // select a random command for the node + int command_id = RandomCommand(rate, depth, must_be_modifier); + // create the node and add it to the graph + sg::Node command_info(command_id, sg::kCommand, rate); + sg::Vertex vertex = boost::add_vertex(command_info, graph); + // a modifier command has to have further nodes added to its main input + const Command& command = commands_[command_id]; + for (int i = 0; i < command.Arguments().size(); i++) { + const Argument& argument = command.GetArgument(i); + if (argument.name_ == "in") { + if (make_modifier_input) { + // modifier input must be given a command tree + sg::Vertex tree = RandomTree(graph, rate, kTreeMode_Command, depth + 1); + sg::Connection connection(i); + GenerateWeightAndOffset(argument, rate, &connection); + boost::add_edge(tree, vertex, connection, graph); + } + } else if (argument.scaling_mode_ == sc::Argument::kConstant) { + // don't attach inputs to predefined arguments + continue; + } else if (stdx::Chance(g_probability_argument_affected)) { + // randomly create input for command arguments + sg::Vertex tree = RandomTree(graph, + sg::kControlRate, + kTreeMode_Mod, + depth + 1); + sg::Connection connection(i); + GenerateWeightAndOffset(argument, rate, &connection); + boost::add_edge(tree, vertex, connection, graph); + } + } + return vertex; +} + +sg::Vertex Grammar::RandomTree(sg::Graph &graph, + sg::CommandRate rate, + TreeMode tree_mode, + int depth) const { + enum NodeType { + kCommand, + kSpecial, + kConstant, + kParameter + }; + static stats::ProbabilitySelector node_type_selector; + if (!node_type_selector.Initialized()) { + std::vector<double> probabilities(3); + probabilities[kCommand] = g_probability_mod_node_command; + probabilities[kSpecial] = g_probability_mod_node_special; + probabilities[kConstant] = g_probability_mod_node_constant; + node_type_selector.SetProbabilities(probabilities); + } + // establish the type of node to create + int node_type; + if (depth >= max_depth_) { + if (tree_mode == kTreeMode_Command) { + // if we're at maximum depth in command mode then create a command + node_type = kCommand; + } else { + // in mod mode at maximum depth pick either a constant or parameter + if (stdx::Chance(g_probability_mod_node_constant)) { + node_type = kConstant; + } else { + node_type = kParameter; + } + } + } else { + if (tree_mode == kTreeMode_Command) { + if (stdx::Chance(g_probability_command_node_command)) { + node_type = kCommand; + } else { + node_type = kSpecial; + } + } else { + // mod mode below max depth, any node type is ok + node_type = static_cast<NodeType>(node_type_selector()); + } + } + if (node_type == kCommand) { + return CreateCommand(graph, rate, depth); + } else if (node_type == kSpecial) { + int command = stdx::Random(Command::kNumberOfSpecialCommands); + return SpecialCommand(command, graph, rate, tree_mode, depth + 1); + } else if (node_type == kConstant) { + return boost::add_vertex(sg::Node(stdx::RandomCoefficient()), graph); + } else if (node_type == kParameter) { + return boost::add_vertex(sg::Node(RandomParameter(graph), sg::kParameter), + graph); + } + throw std::runtime_error("Grammar::RandomTree - unknown node type"); +} + +int Grammar::RandomParameter(sg::Graph& graph) const { + // as a simple rule, randomly pick an existing parameter, or if chosen + // parameter is greater than # of parameters, then add a new one + std::vector<double>& parameters = graph[boost::graph_bundle].parameters_; + int number_of_parameters = static_cast<int>(parameters.size()); + // parameter ids start at 1 + int parameter = stdx::RandomRangeInt(1, number_of_parameters + 1); + // bump parameter count if we've got a new parameter + if (parameter > number_of_parameters) { + // store a random value for the new parameter + parameters.push_back(stdx::RandomCoefficient()); + } + return parameter; +} + +void Grammar::AddTreeToSpecialCommand(sg::Vertex command_node, + sg::Graph& graph, + int input_id, + TreeMode tree_mode, + sg::CommandRate command_rate, + bool* constant_added, + std::set<int>* parameters, + int depth) const { + bool success = false; + while (!success) { + sg::Vertex tree; + // first channel or audio command should enforce a command source + if (input_id == 0) { + tree = RandomTree(graph, command_rate, kTreeMode_Command, depth + 1); + } else { + tree = RandomTree(graph, command_rate, tree_mode, depth + 1); + } + sg::NodeType tree_type = graph[tree].type_; + if (tree_type == sg::kConstant) { + // we only need one constant per mixer + if (*constant_added) { + RemoveSubTree(tree, graph); + continue; + } + *constant_added = true; + } else if (tree_type == sg::kParameter) { + // we don't want duplicated parameters for special command inputs + if (parameters->find(graph[tree].id_) != parameters->end()) { + RemoveSubTree(tree, graph); + continue; + } + // store the parameter id + parameters->insert(graph[tree].id_); + } + // apply weighting + double weight = stdx::RandomCoefficient(); + if (command_rate == sg::kAudioRate) { + // scale audio to curve + weight *= weight; + } + // make the connection to the generated tree + boost::add_edge(tree, command_node, sg::Connection(weight), graph); + success = true; + } +} + +sg::Vertex Grammar::SpecialCommand(int command_id, + sg::Graph& graph, + sg::CommandRate command_rate, + TreeMode tree_mode, + int depth /* = 0 */, + int minimum_channels /* = 2 */) const { + // create the special command node + sg::Node command_info(command_id, sg::kSpecial, command_rate); + sg::Vertex mixer = boost::add_vertex(command_info, graph); + // pick a random number of channels to create for this command + int channels = stdx::RandomRangeInt(minimum_channels, + g_maximum_mixer_channels); + bool constant_added = false; + std::set<int> parameters; + // add a tree for each channel + for (int i = 0; i < channels; i++) { + AddTreeToSpecialCommand(mixer, + graph, + i, + tree_mode, + command_rate, + &constant_added, + ¶meters, + depth); + } + return mixer; +} + +void Grammar::RandomGraph(sg::Graph& graph) const { + graph.clear(); + // root output node + SpecialCommand(sc::Command::kMixer, graph, sg::kAudioRate, kTreeMode_Command); +} + +void Grammar::PickCrossoverNodes(const sg::Graph& parent_1, + const sg::Graph& parent_2, + sg::Vertex* crossover_node_1, + sg::Vertex* crossover_node_2) const { + sg::Vertex source_node_1; + sg::Vertex source_node_2; + bool nodes_found = false; + do { + // pick random edges and their nodes + source_node_1 = boost::random_vertex(parent_1, bx::RandomEngine()); + source_node_2 = boost::random_vertex(parent_2, bx::RandomEngine()); + // avoid the root output node + if ((source_node_1 == 0) || (source_node_2 == 0)) { + continue; + } + // check it's ok to swap these nodes + const sg::Node& info_1 = parent_1[source_node_1]; + const sg::Node& info_2 = parent_2[source_node_2]; + // command rates need to match + if (info_1.rate_ != info_2.rate_) { + continue; + } + // we don't want to swap two constants + if (info_1.type_ == sg::kConstant && info_2.type_ == sg::kConstant) { + continue; + } + // we don't want to swap parameter inputs with same parameter id + if ((info_1.type_ == sg::kParameter) + && (info_2.type_ == sg::kParameter) + && (info_1.id_ == info_2.id_)) { + continue; + } + // check that we're not replacing a control modifer's input with non-command + sg::Edge connection_1 = *(boost::out_edges(source_node_1, parent_1).first); + sg::Edge connection_2 = *(boost::out_edges(source_node_2, parent_2).first); + sg::Vertex target_node_1 = boost::target(connection_1, parent_1); + sg::Vertex target_node_2 = boost::target(connection_2, parent_2); + const sg::Node& info_target_1 = parent_1[target_node_1]; + const sg::Node& info_target_2 = parent_2[target_node_2]; + if ((info_target_1.type_ == sg::kCommand) + || (info_target_1.type_ == sg::kSpecial)) { + const sc::Command& target_command_1 = commands_[info_target_1.id_]; + const std::vector<Argument>& arguments_1 = target_command_1.Arguments(); + const sg::Connection& info_connection_1 = parent_1[connection_1]; + if (arguments_1[info_connection_1.input_].name_ == "in") { + // target input is a modifier's 'in' argument, only allow commands to + // be swapped in + if (!(info_2.type_ == sg::kCommand || info_2.type_ == sg::kSpecial)) { + continue; + } + } + } + if ((info_target_2.type_ == sg::kCommand) + || (info_target_2.type_ == sg::kSpecial)) { + const sc::Command& target_command_2 = commands_[info_target_2.id_]; + const std::vector<Argument>& arguments_2 = target_command_2.Arguments(); + const sg::Connection& info_connection_2 = parent_1[connection_2]; + if (arguments_2[info_connection_2.input_].name_ == "in") { + // target input is a modifier's 'in' argument, only allow commands to + // be swapped in + if (!(info_1.type_ == sg::kCommand || info_1.type_ == sg::kSpecial)) { + continue; + } + } + } + nodes_found = true; + } while (!nodes_found); + *crossover_node_1 = source_node_1; + *crossover_node_2 = source_node_2; +} + +bool Grammar::MutationReplaceSubTree(sg::Graph& graph) const { + // sanity check + if (boost::num_vertices(graph) <= 1) { + throw std::runtime_error("sc::Grammar::MutationReplaceSubTree - graph has too few nodes"); + } + // pick node to replace + sg::Vertex node_to_replace; + do { + node_to_replace = boost::random_vertex(graph, bx::RandomEngine()); + } while (node_to_replace == 0); + sg::CommandRate new_tree_rate = graph[node_to_replace].rate_; + sg::Edge connection = *(boost::out_edges(node_to_replace, graph).first); + sg::Vertex connection_target = boost::target(connection, graph); + // store the connection info contained in the connection edge + sg::Connection connection_info = graph[connection]; + const sg::Node& target_info = graph[connection_target]; + // determine new tree mode + TreeMode new_tree_mode = kTreeMode_Mod; + if (new_tree_rate == sg::kAudioRate) { + // audio rate nodes always require command tree replacements + new_tree_mode = kTreeMode_Command; + } else if (target_info.type_ == sg::kCommand) { + // if the connection target is a modifier + // and the connection input is 0 then a command tree is needed + if (commands_[target_info.id_].Mode() == Command::kModifier) { + if (connection_info.input_ == 0) { + new_tree_mode = kTreeMode_Command; + } + } + } else if (target_info.type_ == sg::kSpecial) { + // first input to special commands is a command tree + if (connection_info.input_ == 0) { + new_tree_mode = kTreeMode_Command; + } + } + // remove the sub tree + RemoveSubTree(connection, graph); + // generate new tree + sg::Vertex new_tree = RandomTree(graph, new_tree_rate, new_tree_mode, 0); + // connect tree to graph + boost::add_edge(new_tree, connection_target, connection_info, graph); + return true; +} + + + +bool Grammar::MutationAddSubTree(sg::Graph& graph) const { + // sanity check + if (graph[0].type_ != sg::kSpecial) { + throw std::runtime_error("sc::Grammar::MutationAddSubTree - missing root mixer node"); + } + bool mutation_occurred = false; + while (!mutation_occurred) { + // pick a command or special node to add a tree input to + sg::Vertex node; + sg::NodeType node_type; + do { + node = boost::random_vertex(graph, bx::RandomEngine()); + node_type = graph[node].type_; + } while (!(node_type == sg::kCommand || node_type == sg::kSpecial)); + if (node_type == sg::kCommand) { + int node_id = graph[node].id_; + const std::vector<Argument>& arguments = commands_[node_id].Arguments(); + std::size_t number_of_arguments = arguments.size(); + sg::InEdgeIterator edge; + sg::InEdgeIterator edge_end; + std::vector<bool> used_arguments(arguments.size(), false); + for (boost::tie(edge, edge_end) = boost::in_edges(node, graph); + edge != edge_end; + ++edge) { + used_arguments[graph[*edge].input_] = true; + } + // check that there is an unused argument available for this command + bool argument_available = false; + for (std::size_t i = 0; i < number_of_arguments; i++) { + if (used_arguments[i] == false) { + // we don't want to attach a tree to a preset argument + if (arguments[i].scaling_mode_ != Argument::kConstant) { + argument_available = true; + break; + } + } + } + if (argument_available) { + // find a random argument to insert a tree into + std::size_t argument; + do { + argument = stdx::Random(arguments.size()); + } while (used_arguments[argument] == true + || arguments[argument].scaling_mode_ == Argument::kConstant); + // add a tree and connect to the unused argument + sg::Vertex tree = RandomTree(graph, + sg::kControlRate, + kTreeMode_Mod, + 0); + sg::Connection connection(static_cast<int>(argument)); + GenerateWeightAndOffset(arguments[argument], + sg::kControlRate, + &connection); + boost::add_edge(tree, node, connection, graph); + mutation_occurred = true; + } + } else if (node_type == sg::kSpecial) { + int node_inputs = static_cast<int>(boost::in_degree(node, graph)); + // we need to check the special command's inputs to avoid adding redundant + // constants or parameters + bool constant_added = false; + std::set<int> parameters; + sg::InEdgeIterator edge; + sg::InEdgeIterator edge_end; + for (boost::tie(edge, edge_end) = boost::in_edges(node, graph); + edge != edge_end; + ++edge) { + const sg::Node& source = graph[boost::source(*edge, graph)]; + if (source.type_ == sg::kConstant) { + constant_added = true; + } + if (source.type_ == sg::kParameter) { + parameters.insert(source.id_); + } + } + // select tree mode based on command rate + TreeMode tree_mode; + if (graph[node].rate_ == sg::kAudioRate) { + tree_mode = kTreeMode_Command; + } else { + tree_mode = kTreeMode_Mod; + } + // add the tree + AddTreeToSpecialCommand(node, + graph, + node_inputs, + tree_mode, + graph[node].rate_, + &constant_added, + ¶meters, + 0); + mutation_occurred = true; + } + } + return true; +} + +bool Grammar::MutationModifyInputs(sg::Graph& graph) const { + // sanity check + if (graph[boost::graph_bundle].parameters_.size() == 0) { + sg::VertexIterator vertex; + sg::VertexIterator vertex_end; + bool constant_found = false; + for (boost::tie(vertex, vertex_end) = boost::vertices(graph); + vertex != vertex_end; + ++vertex) { + sg::Node& node = graph[*vertex]; + if (node.type_ == sg::kConstant) { + constant_found = true; + break; + } + } + if (!constant_found) { + // no parameters, no constants, nothing to do.. + return false; + } + } + // make a random generator with a narrow guassian distribution + bx::GaussianGenerator gaussian = bx::MakeGaussianGenerator(0, 0.125); + bool mutation_occurred = false; + while (!mutation_occurred) { + // mutate parameters + foreach (double& parameter, graph[boost::graph_bundle].parameters_) { + if (stdx::Chance(g_probability_input_mutation)) { + MutateValue(¶meter, &mutation_occurred); + } + } + // mutate constants + sg::VertexIterator vertex; + sg::VertexIterator vertex_end; + for (boost::tie(vertex, vertex_end) = boost::vertices(graph); + vertex != vertex_end; + ++vertex) { + sg::Node& node = graph[*vertex]; + if (node.type_ == sg::kConstant) { + // constant value + if (stdx::Chance(g_probability_input_mutation)) { + MutateValue(&node.constant_value_, &mutation_occurred); + } + } + } + } + return true; +} + +bool Grammar::MutationModifyConnections(sg::Graph& graph) const { + // sanity check + if (boost::num_edges(graph) < 1) { + throw std::runtime_error("sc::Grammar::MutationModifyConnections - graph has no connections"); + } + bx::GaussianGenerator gaussian = bx::MakeGaussianGenerator(0, 0.125); + bool mutation_occurred = false; + while (!mutation_occurred) { + sg::EdgeIterator edge; + sg::EdgeIterator edge_end; + for (boost::tie(edge, edge_end) = boost::edges(graph); + edge != edge_end; + ++edge) { + if (stdx::Chance(g_probability_connection_mutation)) { + sg::Connection& connection = graph[*edge]; + const sg::Node& target = graph[boost::target(*edge, graph)]; + const sc::Argument& argument = GetCommandArgument(target.id_, + connection.input_); + if (!argument.fixed_range_) { + // mutate weight + MutateValue(&connection.weight_, + &mutation_occurred, + g_minimum_connection_weight); + // special command inputs and modifier command input arguments + // have offsets set to zero + if ((target.type_ != sg::kSpecial) && (argument.name_ != "in")) { + MutateValue(&connection.offset_, &mutation_occurred, + 0.0, 1.0 - connection.weight_); + } + } + } + } + } + return true; +} + +const std::vector<sc::Argument>& Grammar::CommandArguments(int command) const { + return commands_[command].Arguments(); +} + +bool Grammar::MutationReplaceCommand(sg::Graph& graph) const { + // sanity check + if (boost::num_vertices(graph) <= 1) { + throw std::runtime_error("sc::Grammar::MutationReplaceCommand - graph has too few nodes"); + } + sg::Vertex node_to_replace; + sg::Node* node_info; + // find command to replace + do { + node_to_replace = boost::random_vertex(graph, bx::RandomEngine()); + node_info = &graph[node_to_replace]; + } while (node_info->type_ != sg::kCommand); + int old_id = node_info->id_; + // generate new command id + int new_id; + int number_of_commands = static_cast<int>(commands_.size()); + do { + // new command must match old output and mode + new_id = stdx::Random(number_of_commands); + } while (new_id != old_id + && commands_[new_id].Output() != commands_[old_id].Output() + && commands_[new_id].Mode() != commands_[old_id].Mode()); + // new command info + const std::vector<sc::Argument>& arguments = CommandArguments(new_id); + std::size_t number_of_arguments = commands_[new_id].Arguments().size(); + // remove invalid parameters + // removing an in edge invalidates the iterators, so we need to restart the + // search each time an edge is removed + sg::InEdgeIterator edge; + sg::InEdgeIterator edge_end; + bool do_it_again; + do { + do_it_again = false; + for (boost::tie(edge, edge_end) = boost::in_edges(node_to_replace, graph); + edge != edge_end; + ++edge) { + int input = graph[*edge].input_; + // remove input tree if it's for an extraneous parameter + // or if the argument is a constant + if (input >= number_of_arguments + || arguments[input].scaling_mode_ == sc::Argument::kConstant) { + RemoveSubTree(*edge, graph); + do_it_again = true; + // break out of for loop + break; + } + } + } while (do_it_again); + // now invalid parameters have been removed, validate the rest + for (boost::tie(edge, edge_end) = boost::in_edges(node_to_replace, graph); + edge != edge_end; + ++edge) { + // if the new argument is fixed range then correct the connection weight + int input = graph[*edge].input_; + if (arguments[input].fixed_range_) { + GenerateWeightAndOffset(arguments[input], + node_info->rate_, + &graph[*edge]); + } + } + // apply new command id + node_info->id_ = new_id; + return true; +} + +int Grammar::GetCommandInputID(int command) const { + const std::vector<sc::Argument>& arguments = commands_[command].Arguments(); + for (std::size_t i = 0, size = arguments.size(); i < size; i++) { + if (arguments[i].name_ == "in") { + return static_cast<int>(i); + } + } + return -1; +} + +const Argument& Grammar::GetCommandArgument(int command_id, + int argument_id) const { + return commands_[command_id].Arguments()[argument_id]; +} + +bool Grammar::MutationInsertCommand(sg::Graph& graph) const { + // sanity check + if (boost::num_vertices(graph) == 0) { + throw std::runtime_error("sc::Grammar::MutationInsertCommand - graph is empty"); + } + // pick a random edge as insertion point + sg::Edge edge_to_replace; + sg::Vertex source_command; + sg::NodeType source_type; + do { + edge_to_replace = boost::random_edge(graph, bx::RandomEngine()); + source_command = boost::source(edge_to_replace, graph); + source_type = graph[source_command].type_; + } while (source_type != sg::kCommand); + // store target connection info + sg::Vertex target_command = boost::target(edge_to_replace, graph); + sg::Connection target_connection = graph[edge_to_replace]; + // make the new command + const sg::Node& source_node = graph[source_command]; + sg::CommandRate rate = source_node.rate_; + sg::Vertex new_command = CreateCommand(graph, rate, 0, true, false); + // remove the existing connection + boost::remove_edge(source_command, target_command, graph); + // connect the new command to the source + sg::Connection source_connection(GetCommandInputID(graph[new_command].id_)); + // generate weighting for new connection + double weight = stdx::RandomCoefficient(); + if (rate == sg::kAudioRate) { + weight *= weight; + } + source_connection.weight_ = weight; + boost::add_edge(source_command, new_command, source_connection, graph); + // connect new command to the target, use old connection weighting + boost::add_edge(new_command, target_command, target_connection, graph); + // success! + return true; +} + +} // sc namespace
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/src/sc_grammar.hpp Thu Aug 25 11:05:55 2011 +0100 @@ -0,0 +1,175 @@ +// Copyright 2011, Ian Hobson. +// +// This file is part of gpsynth. +// +// gpsynth is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// gpsynth is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with gpsynth in the file COPYING. +// If not, see http://www.gnu.org/licenses/. + +// SuperCollider grammar, resonsible for generating and altering SuperCollider +// synth graphs + +#pragma once + +#include "grammar.hpp" +#include "range.hpp" +#include "synth_graph.hpp" + +#include <map> +#include <string> +#include <vector> + +namespace sc { + +struct Argument { + + enum ScalingMode { + kScalingLinear, + kScalingLog, + kConstant + }; + + std::string name_; + stdx::Range<double> range_; + stdx::Range<double> range_control_; + ScalingMode scaling_mode_; + double constant_value_; + bool fixed_range_; + + Argument(const std::string name = "") + : name_(name), + range_(0, 1), + range_control_(0, 1), + scaling_mode_(kScalingLinear), + constant_value_(0), + fixed_range_(false) + {} +}; + +class Command { +public: + enum CommandMode { + kSource, + kModifier, + kSpecial, + kNumberOfModes + }; + + enum SpecialCommands { + kMixer, + kMultiplier, + kNumberOfSpecialCommands + }; + + enum OutputType { + kAll, + kAudio, + kControl, + }; + +private: + std::string name_; + std::vector<Argument> arguments_; + CommandMode mode_; + OutputType output_; + +public: + Command(const std::string& name, + CommandMode mode, + OutputType output = kAll, + const std::vector<Argument>& arguments = std::vector<Argument>()) + : name_(name), + mode_(mode), + output_(output), + arguments_(arguments) + {} + + CommandMode Mode() const { return mode_; } + OutputType Output() const { return output_; } + const std::string& Name() const { return name_; } + const std::vector<Argument>& Arguments() const { return arguments_;} + const Argument& GetArgument(int argument) const { + return arguments_[argument]; + } + bool IsAudioSource() { return (mode_ == kSource) && (output_ != kControl); } +}; + +class Grammar : public GrammarInterface { + // holds the commands available to the grammar + std::vector<Command> commands_; + typedef std::vector<Command>::size_type CommandID; + int max_depth_; + +public: + // used when generating trees to indicate current tree state + enum TreeMode { + kTreeMode_Command, + kTreeMode_Mod + }; + + Grammar(const std::string& json_data); + + void ParseJSON(const std::string& json_data); + + const std::vector<Command>& Commands() const { return commands_; } + + void SetMaximumDepth(int max_depth) { max_depth_ = max_depth; } + + void RandomGraph(sg::Graph& graph) const; + void PickCrossoverNodes(const sg::Graph& parent_1, + const sg::Graph& parent_2, + sg::Vertex* crossover_node_1, + sg::Vertex* crossover_node_2) const; + bool MutationReplaceSubTree(sg::Graph& graph) const; + bool MutationAddSubTree(sg::Graph& graph) const; + bool MutationModifyInputs(sg::Graph& graph) const; + bool MutationModifyConnections(sg::Graph& graph) const; + bool MutationReplaceCommand(sg::Graph& graph) const; + bool MutationInsertCommand(sg::Graph& graph) const; + +private: + int RandomCommand(sg::CommandRate rate, + int tree_depth, + bool must_be_modifier) const; + int RandomParameter(sg::Graph& graph) const; + + sg::Vertex RandomTree(sg::Graph& graph, + sg::CommandRate rate, + TreeMode tree_mode, + int depth) const; + + sg::Vertex SpecialCommand(int command_id, + sg::Graph& graph, + sg::CommandRate output_mode, + TreeMode tree_mode, + int depth = 0, + int minimum_channels = 2) const; + void AddTreeToSpecialCommand(sg::Vertex command, + sg::Graph& graph, + int input_id, + TreeMode tree_mode, + sg::CommandRate command_rate, + bool* constant_added, + std::set<int>* parameters, + int depth) const; + sg::Vertex CreateCommand(sg::Graph &graph, + sg::CommandRate rate, + int depth, + bool must_be_modifier = false, + bool make_modifier_input = true) const; + int GetCommandInputID(int command_id) const; + const Argument& GetCommandArgument(int command_id, int argument_id) const; + const std::vector<sc::Argument>& CommandArguments(int command) const; +}; + +} // sc namespace
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/src/spectrum_analyzer.hpp Thu Aug 25 11:05:55 2011 +0100 @@ -0,0 +1,99 @@ +// Copyright 2011, Ian Hobson. +// +// This file is part of gpsynth. +// +// gpsynth is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// gpsynth is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with gpsynth in the file COPYING. +// If not, see http://www.gnu.org/licenses/. + +// Wrapper classes for FFTW + +#pragma once + +#include "fftw3.h" + +#include <complex> + +namespace dsp { + +// Float version +class SpectrumAnalyzerFloat { + fftwf_plan plan_; + int frame_size_; + float* input_; + std::complex<float>* output_; + +public: + SpectrumAnalyzerFloat(int frame_size = 1024) + : frame_size_(frame_size) + { + input_ = (float*)fftw_malloc(sizeof(float) * frame_size); + int buffer_size = sizeof(std::complex<float>) * frame_size / 2; + output_ = (std::complex<float>*)fftw_malloc(buffer_size); + plan_ = fftwf_plan_dft_r2c_1d(frame_size, + input_, + reinterpret_cast<fftwf_complex*>(output_), + FFTW_ESTIMATE); + } + + SpectrumAnalyzerFloat() { + fftw_free(input_); + fftw_free(output_); + fftwf_destroy_plan(plan_); + } + + void Execute() { + fftwf_execute(plan_); + } + + int Size() { return frame_size_; } + float* Input() { return input_; } + std::complex<float>* Output() { return output_; } +}; + +// Double version +class SpectrumAnalyzer { + fftw_plan plan_; + int frame_size_; + double* input_; + std::complex<double>* output_; + +public: + SpectrumAnalyzer(int frame_size = 1024) + : frame_size_(frame_size) + { + input_ = (double*)fftw_malloc(sizeof(double) * frame_size); + int buffer_size = sizeof(std::complex<double>) * frame_size; + output_ = (std::complex<double>*)fftw_malloc(buffer_size); + plan_ = fftw_plan_dft_r2c_1d(frame_size, + input_, + reinterpret_cast<fftw_complex*>(output_), + FFTW_ESTIMATE); + } + + SpectrumAnalyzer() { + fftw_free(input_); + fftw_free(output_); + fftw_destroy_plan(plan_); + } + + void Execute() { + fftw_execute(plan_); + } + + int Size() { return frame_size_; } + double* Input() { return input_; } + std::complex<double>* Output() { return output_; } +}; + +} // dsp namespace
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/src/statistics.hpp Thu Aug 25 11:05:55 2011 +0100 @@ -0,0 +1,139 @@ +// Copyright 2011, Ian Hobson. +// +// This file is part of gpsynth. +// +// gpsynth is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// gpsynth is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with gpsynth in the file COPYING. +// If not, see http://www.gnu.org/licenses/. + +// Some useful stats functions + +#pragma once + +#include "range.hpp" +#include "std_ex.hpp" + +#include <algorithm> +#include <iterator> +#include <numeric> +#include <sstream> +#include <stdexcept> + +namespace stats { + +// Sum +template<typename Iterator> +typename std::iterator_traits<Iterator>::value_type Sum(Iterator start, + Iterator end) { + typename std::iterator_traits<Iterator>::value_type initializer(0); + return std::accumulate(start, end, initializer); +} + +// Sum - container +template<typename Container> +typename Container::value_type Sum(const Container& container) { + return Sum(container.begin(), container.end()); +} + +// Mean +template<typename Iterator> +typename std::iterator_traits<Iterator>::value_type Mean(Iterator start, + Iterator end) { + return Sum(start, end) / std::distance(start, end); +} + +// Mean - container +template<typename Container> +typename Container::value_type Mean(const Container& container) { + return Mean(container.begin(), container.end()); +} + +// measures the MSE between a range and a target +template<typename Iterator1, typename Iterator2> +typename std::iterator_traits<Iterator1>::value_type +MeanSquaredError(Iterator1 start1, Iterator1 end1, Iterator2 start2) { + typedef typename std::iterator_traits<Iterator1>::value_type Value; + Value n = std::distance(start1, end1); + Value error(0); + while (start1 != end1) { + error += std::pow(*start1 - *start2, Value(2)); + ++start1; + ++start2; + } + return error / n; +} + +// MeanSquaredError - container adaptor +template<typename Container> +typename Container::value_type MeanSquaredError(const Container& x, + const Container& y) { + return MeanSquaredError(x.begin(), x.end(), y.begin()); +} + +// root mean square error +template<typename Iterator1, typename Iterator2> +double RMSE(Iterator1 start, Iterator1 end, Iterator2 target) { + return std::sqrt(MeanSquaredError(start, end, target)); +} + +// normalized root mean square error, using precomputed minima and maxima +template<typename Iterator1, typename Iterator2, typename T> +T NRMSE(Iterator1 start, Iterator1 end, Iterator2 target, + const stdx::Range<T>& range1, const stdx::Range<T>& range2) { + T range = std::max(range1.Maximum(), range2.Maximum()) + - std::min(range1.Minimum(), range2.Minimum()); + return RMSE(start, end, target) / range; +} + +// Takes a set of numbers with sum <= 1 which define a distribution. +// operator() returns index chosen randomly with distribution defined by the +// provided probabilites. +// if the probabilites have sum < 1 then the difference is taken to imply a +// single additional index +// e.g. given: +// ProbabilitySelector selector(boost::assign::list_of(0.1)(0.8)); +// selector() will yield +// '0' 10%, +// '1' 80%, +// '2' 10% +class ProbabilitySelector { + std::vector<double> boundaries_; + +public: + ProbabilitySelector() {} + ProbabilitySelector(const std::vector<double>& probabilities) + { + SetProbabilities(probabilities); + } + + void SetProbabilities(const std::vector<double>& probabilities) { + if (std::count_if(probabilities.begin(), probabilities.end(), + stdx::LessThan<double>(0.0))) { + throw std::runtime_error("ProbabilitySelector: negative value found"); + } + // assign range boundaries + std::partial_sum(probabilities.begin(), probabilities.end(), + std::back_inserter(boundaries_)); + } + + std::size_t operator()() const { + double random = rand() / static_cast<double>(RAND_MAX); + std::vector<double>::const_iterator index; + index = std::upper_bound(boundaries_.begin(), boundaries_.end(), random); + return std::distance(boundaries_.begin(), index); + } + + bool Initialized() const { return !boundaries_.empty(); } +}; + +} // stats namespace
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/src/std_ex.hpp Thu Aug 25 11:05:55 2011 +0100 @@ -0,0 +1,162 @@ +// Copyright 2011, Ian Hobson. +// +// This file is part of gpsynth. +// +// gpsynth is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// gpsynth is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with gpsynth in the file COPYING. +// If not, see http://www.gnu.org/licenses/. + +// Miscellaneous C++ stdlib helper utilities + +#pragma once + +#include <cstdlib> +#include <iostream> +#include <iterator> +#include <fstream> +#include <map> +#include <sstream> +#include <stdexcept> +#include <string> + +namespace stdx { + +typedef unsigned int uint; + +// Helper function for retrieving values from const std::maps +template<typename M> +const typename M::mapped_type& GetFromMap(const M& map, + const typename M::key_type& key) { + typename M::const_iterator i = map.find(key); + if (i == map.end()) { + throw std::runtime_error("GetFromMap: specified key not in map."); + } + return i->second; +} + + +template<typename T> +inline T RandomRange(T min, T max) { + return static_cast<T>(((double)rand() / (float)RAND_MAX) * (max - min) + min); +} + +template<typename T> +inline T RandomRangeInt(T min, T max) { + return static_cast<T>(((double)rand() / (float)RAND_MAX) + * (max - min) + + min + 0.5); +} + +template<typename T> +inline T Random(T maximum) { + return static_cast<T>(((double)rand() / (double)RAND_MAX) * maximum); +} + +inline bool Random5050() { + return rand() > (RAND_MAX / 2); +} + +inline double RandomCoefficient() { + return static_cast<double>(rand()) / static_cast<double>(RAND_MAX); +} + +inline bool Chance(double probability) { + return RandomCoefficient() < probability; +} + +template<typename T> +class RandomGenerator { + T minimum_; + T maximum_; + +public: + RandomGenerator(T minimum = 0, T maximum = 1) + : minimum_(minimum), + maximum_(maximum) + {} + + T operator()() { + return RandomRange(minimum_, maximum_); + } +}; + + +// clamps input T to range specified by U and V +template<typename T, typename U, typename V> +T Clamp(const T& value, const U& minimum, const V& maximum) { + return (value > maximum) ? maximum : (value < minimum) ? minimum : value; +} + +// Function object that increments its internal value by the provided step size +template<typename T> +class Stepper { + T value_; + T step_size_; +public: + Stepper(const T& step_size, const T& initial_value = T()) + : value_(initial_value), + step_size_(step_size) + {} + + T operator()() { + T result = value_; + value_ += step_size_; + return result; + } +}; + +// Function object that returns true if value is less than internal value +template<typename T> +class LessThan { + T value_; +public: + LessThan(const T& value) : value_(value) {} + bool operator()(const T& other) { return other < value_; } +}; + +// writes a container to a stream +template<typename Container> +void DumpContainer(const Container& container, + const std::string& separator = "\n", + std::ostream& output = std::cout) { + typedef typename Container::value_type Value; + std::copy(container.begin(), container.end(), + std::ostream_iterator<Value>(output, separator.c_str())); +} + +// saves a container to a specified file path +template<typename Container> +void SaveContainerToFile(const Container& container, + const std::string& file_path, + const std::string& separator = "\n") { + std::ofstream file(file_path.c_str()); + DumpContainer(container, separator, file); +} + +template<typename T> +void AppendToFile(const T& value_to_append, + const std::string& file_path, + const std::string& separator = "\n") { + std::ofstream file(file_path.c_str(), + std::ofstream::app | std::ofstream::out); + file << value_to_append << separator; +} + +inline std::string LoadFile(const std::string& file_path) { + std::ifstream file(file_path.c_str()); + std::stringstream buffer; + buffer << file.rdbuf(); + return buffer.str(); +} + +} // stdx namespace
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/src/synth_graph.hpp Thu Aug 25 11:05:55 2011 +0100 @@ -0,0 +1,139 @@ +// Copyright 2011, Ian Hobson. +// +// This file is part of gpsynth. +// +// gpsynth is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// gpsynth is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with gpsynth in the file COPYING. +// If not, see http://www.gnu.org/licenses/. + +// The main synth graph classes + +#pragma once + +#include "boost/graph/adjacency_list.hpp" + +#include <string> +#include <vector> + +namespace synth_graph { + +enum NodeType { + kUnknown = -1, + kCommand, + kSpecial, + kConstant, + kParameter +}; + +enum CommandRate { + kAudioRate, + kControlRate +}; + +// Node, attached to each vertex +struct Node { + int id_; + NodeType type_; + CommandRate rate_; + double constant_value_; + + Node() + : id_(-1), + type_(kUnknown), + rate_(kAudioRate), + constant_value_(0) + {} + + Node(int id, + NodeType type, + CommandRate rate = kControlRate) + : id_(id), + type_(type), + rate_(rate), + constant_value_(0) + {} + + Node(double constant_value) + : id_(-1), + type_(kConstant), + rate_(kControlRate), + constant_value_(constant_value) + {} +}; + +// Connection, attached to each edge +struct Connection { + // input that the edge is connected to + int input_; + double weight_; + double offset_; + + Connection(int input = -1, double weight = 1, double offset = 0) + : input_(input), + weight_(weight), + offset_(offset) + {} + + // constructor for connection input is irrelevant + Connection(double weight, double offset = 0) + : input_(0), + weight_(weight), + offset_(offset) + {} + + bool IsActive() const { + return (weight_ != 1) || (offset_ != 0); + } +}; + +enum { + kFitnessUnrated = -1 +}; + +// Properties stored with the graph +struct GraphProperties { + // parameter values + std::vector<double> parameters_; + // graph id + int id_; + // measured fitness + double fitness_; + // rendered output path + std::string render_path_; + // converted graphviz path + std::string dot_path_; + + GraphProperties() + : parameters_(0), + fitness_(kFitnessUnrated), + id_(0) + {} +}; + +// defined as bidirectional to allow access to in_edges of a vertex +// we need to use vecS for storage for to_graphviz +typedef boost::adjacency_list<boost::vecS, + boost::vecS, + boost::bidirectionalS, + Node, + Connection, + GraphProperties> Graph; + +typedef boost::graph_traits<Graph>::vertex_descriptor Vertex; +typedef boost::graph_traits<Graph>::vertex_iterator VertexIterator; +typedef boost::graph_traits<Graph>::edge_descriptor Edge; +typedef boost::graph_traits<Graph>::edge_iterator EdgeIterator; +typedef boost::graph_traits<Graph>::in_edge_iterator InEdgeIterator; + +} // synth_graph namespace +namespace sg = synth_graph;
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/src/window_makers.hpp Thu Aug 25 11:05:55 2011 +0100 @@ -0,0 +1,54 @@ +// Copyright 2011, Ian Hobson. +// +// This file is part of gpsynth. +// +// gpsynth is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// gpsynth is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with gpsynth in the file COPYING. +// If not, see http://www.gnu.org/licenses/. + +#pragma once + +#include <cmath> +#include <iterator> + +namespace dsp { + +template<typename OutputIterator> +void BlackmanWindow(int size, OutputIterator output) { + typedef typename std::iterator_traits<OutputIterator>::value_type T; + T m1 = (M_PI * 2.0) / (size - 1.0); + T m2 = (M_PI * 4.0) / (size - 1.0); + for (T i = 0; i < size; i++) { + *output++ = 0.42 - 0.5 * std::cos(i * m1) + 0.08 * std::cos(i * m2); + } +} + +template<typename OutputIterator> +void HannWindow(int size, OutputIterator output) { + typedef typename std::iterator_traits<OutputIterator>::value_type T; + T m = (M_PI * 2.0) / (size - 1.0); + for (T i = 0; i < size; i++) { + *output++ = 0.5 - 0.5 * std::cos(i * m); + } +} + +template<typename OutputIterator> +void HammingWindow(int size, OutputIterator output) { + typedef typename std::iterator_traits<OutputIterator>::value_type T; + T m = (M_PI * 2.0) / (size - 1.0); + for (T i = 0; i < size; i++) { + *output++ = 0.54 - 0.46 * std::cos(i * m); + } +} + +};
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/third_party/boost/process.hpp Thu Aug 25 11:05:55 2011 +0100 @@ -0,0 +1,25 @@ +// +// Boost.Process +// ~~~~~~~~~~~~~ +// +// Copyright (c) 2006, 2007 Julio M. Merino Vidal +// Copyright (c) 2008 Ilya Sokolov, Boris Schaeling +// Copyright (c) 2009 Boris Schaeling +// Copyright (c) 2010 Felipe Tanus, Boris Schaeling +// +// Distributed under the Boost Software License, Version 1.0. (See accompanying +// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) +// + +/** + * \file boost/process.hpp + * + * Convenience header that includes all public Boost.Process header files. + */ + +#ifndef BOOST_PROCESS_HPP +#define BOOST_PROCESS_HPP + +#include <boost/process/all.hpp> + +#endif
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/third_party/boost/process/all.hpp Thu Aug 25 11:05:55 2011 +0100 @@ -0,0 +1,40 @@ +// +// Boost.Process +// ~~~~~~~~~~~~~ +// +// Copyright (c) 2006, 2007 Julio M. Merino Vidal +// Copyright (c) 2008 Ilya Sokolov, Boris Schaeling +// Copyright (c) 2009 Boris Schaeling +// Copyright (c) 2010 Felipe Tanus, Boris Schaeling +// +// Distributed under the Boost Software License, Version 1.0. (See accompanying +// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) +// + +/** + * \file boost/process/all.hpp + * + * Convenience header that includes all public Boost.Process header files. + */ + +#ifndef BOOST_PROCESS_ALL_HPP +#define BOOST_PROCESS_ALL_HPP + +#include <boost/process/child.hpp> +#include <boost/process/context.hpp> +#include <boost/process/environment.hpp> +#include <boost/process/handle.hpp> +#include <boost/process/operations.hpp> +#include <boost/process/pid_type.hpp> +#include <boost/process/pipe.hpp> +#include <boost/process/pistream.hpp> +#include <boost/process/postream.hpp> +#include <boost/process/process.hpp> +#include <boost/process/self.hpp> +#include <boost/process/status.hpp> +#include <boost/process/stream_behavior.hpp> +#include <boost/process/stream_ends.hpp> +#include <boost/process/stream_id.hpp> +#include <boost/process/stream_type.hpp> + +#endif
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/third_party/boost/process/child.hpp Thu Aug 25 11:05:55 2011 +0100 @@ -0,0 +1,92 @@ +// +// Boost.Process +// ~~~~~~~~~~~~~ +// +// Copyright (c) 2006, 2007 Julio M. Merino Vidal +// Copyright (c) 2008 Ilya Sokolov, Boris Schaeling +// Copyright (c) 2009 Boris Schaeling +// Copyright (c) 2010 Felipe Tanus, Boris Schaeling +// +// Distributed under the Boost Software License, Version 1.0. (See accompanying +// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) +// + +/** + * \file boost/process/child.hpp + * + * Includes the declaration of the child class. + */ + +#ifndef BOOST_PROCESS_CHILD_HPP +#define BOOST_PROCESS_CHILD_HPP + +#include <boost/process/config.hpp> + +#if defined(BOOST_POSIX_API) +#elif defined(BOOST_WINDOWS_API) +# include <windows.h> +#else +# error "Unsupported platform." +#endif + +#include <boost/process/process.hpp> +#include <boost/process/pid_type.hpp> +#include <boost/process/stream_id.hpp> +#include <boost/process/handle.hpp> +#include <map> + +namespace boost { +namespace process { + +/** + * The child class provides access to a child process. + */ +class child : public process +{ +public: + /** + * Creates a new child object that represents the just spawned child + * process \a id. + */ + child(pid_type id, std::map<stream_id, handle> handles) + : process(id), + handles_(handles) + { + } + +#if defined(BOOST_WINDOWS_API) + /** + * Creates a new child object that represents the just spawned child + * process \a id. + * + * This operation is only available on Windows systems. + */ + child(handle hprocess, std::map<stream_id, handle> handles) + : process(hprocess), + handles_(handles) + { + } +#endif + + /** + * Gets a handle to a stream attached to the child. + * + * If the handle doesn't exist an invalid handle is returned. + */ + handle get_handle(stream_id id) const + { + std::map<stream_id, handle>::const_iterator it = handles_.find(id); + return (it != handles_.end()) ? it->second : handle(); + } + +private: + /** + * Handles providing access to streams attached to the child process. + */ + std::map<stream_id, handle> handles_; +}; + +} +} + +#endif
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/third_party/boost/process/config.hpp Thu Aug 25 11:05:55 2011 +0100 @@ -0,0 +1,74 @@ +// +// Boost.Process +// ~~~~~~~~~~~~~ +// +// Copyright (c) 2006, 2007 Julio M. Merino Vidal +// Copyright (c) 2008 Ilya Sokolov, Boris Schaeling +// Copyright (c) 2009 Boris Schaeling +// Copyright (c) 2010 Felipe Tanus, Boris Schaeling +// +// Distributed under the Boost Software License, Version 1.0. (See accompanying +// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) +// + +/** + * \file boost/process/config.hpp + * + * Defines macros that are used by the library to determine the operating + * system it is running under and the features it supports. + */ + +#ifndef BOOST_PROCESS_CONFIG_HPP +#define BOOST_PROCESS_CONFIG_HPP + +#include <boost/config.hpp> +#include <boost/system/config.hpp> +#include <boost/system/system_error.hpp> +#include <boost/throw_exception.hpp> + +#if defined(BOOST_POSIX_API) +# include <errno.h> +#elif defined(BOOST_WINDOWS_API) +# include <windows.h> +#endif + +#if defined(BOOST_POSIX_API) || defined(BOOST_PROCESS_DOXYGEN) +# if !defined(BOOST_PROCESS_POSIX_PATH_MAX) || defined(BOOST_PROCESS_DOXYGEN) +/** + * Specifies the system's maximal supported path length. + * + * The macro BOOST_PROCESS_POSIX_PATH_MAX is set to a positive integer + * value which specifies the system's maximal supported path length. It is + * only used if neither PATH_MAX nor _PC_PATH_MAX and HAVE_PATHCONF are defined. + * The maximal supported path length is required by + * boost::process::self::get_work_dir(). Please note that this function is + * also called by the constructor of boost::process::context. + */ +# define BOOST_PROCESS_POSIX_PATH_MAX 259 +# endif +#endif + +/** \cond */ +#define BOOST_PROCESS_SOURCE_LOCATION \ + "in file '" __FILE__ "', line " BOOST_STRINGIZE(__LINE__) ": " + +#if defined(BOOST_POSIX_API) +# define BOOST_PROCESS_LAST_ERROR errno +#elif defined(BOOST_WINDOWS_API) +# define BOOST_PROCESS_LAST_ERROR GetLastError() +#endif + +#define BOOST_PROCESS_THROW_LAST_SYSTEM_ERROR(what) \ + boost::throw_exception(boost::system::system_error( \ + boost::system::error_code(BOOST_PROCESS_LAST_ERROR, \ + boost::system::get_system_category()), \ + BOOST_PROCESS_SOURCE_LOCATION what)) + +#define BOOST_PROCESS_THROW_ERROR(error, what) \ + boost::throw_exception(boost::system::system_error( \ + boost::system::error_code(error, \ + boost::system::get_system_category()), \ + BOOST_PROCESS_SOURCE_LOCATION what)) +/** \endcond */ + +#endif
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/third_party/boost/process/context.hpp Thu Aug 25 11:05:55 2011 +0100 @@ -0,0 +1,131 @@ +// +// Boost.Process +// ~~~~~~~~~~~~~ +// +// Copyright (c) 2006, 2007 Julio M. Merino Vidal +// Copyright (c) 2008 Ilya Sokolov, Boris Schaeling +// Copyright (c) 2009 Boris Schaeling +// Copyright (c) 2010 Felipe Tanus, Boris Schaeling +// +// Distributed under the Boost Software License, Version 1.0. (See accompanying +// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) +// + +/** + * \file boost/process/context.hpp + * + * Includes the declaration of the context class. + */ + +#ifndef BOOST_PROCESS_CONTEXT_HPP +#define BOOST_PROCESS_CONTEXT_HPP + +#include <boost/process/config.hpp> + +#if defined(BOOST_POSIX_API) +# include <unistd.h> +#elif defined(BOOST_WINDOWS_API) +# include <windows.h> +#endif + +#include <boost/process/stream_id.hpp> +#include <boost/process/stream_ends.hpp> +#include <boost/process/stream_type.hpp> +#include <boost/process/environment.hpp> +#include <boost/process/self.hpp> +#include <boost/process/stream_behavior.hpp> +#include <boost/function.hpp> +#include <string> +#include <map> + +namespace boost { +namespace process { + +/** + * Context class to define how a child process is created. + * + * The context class is used to configure streams, to set the work directory + * and define environment variables. It is also used to change a process + * name (the variable commonly known as argv[0]). + */ +struct context +{ + typedef std::map<stream_id, boost::function<stream_ends (stream_type)> > + streams_t; + + /** + * Streams. + * + * Streams of a child process can be configured through factory functions + * which return a pair of handles - one handle to use as a stream end + * in the child process and possibly another handle to use as a stream end + * in the parent process (if a pipe is setup both processes can communicate + * with each other). + */ + streams_t streams; + + /** + * Process name. + * + * The child process can access the process name via a variable + * commonly known as argv[0]. + */ + std::string process_name; + + /** + * Work directory. + */ + std::string work_dir; + + /** + * Environment variables. + */ + environment env; + + /** + * Constructs a process context. + * + * The default behavior of standard streams is to inherit them. The current + * work directory is also the work directory of the child process. The child + * process also inherits all environment variables. + */ + context() + : work_dir(self::get_work_dir()), + env(self::get_environment()) + { +#if defined(BOOST_POSIX_API) + streams[stdin_id] = behavior::inherit(STDIN_FILENO); + streams[stdout_id] = behavior::inherit(STDOUT_FILENO); + streams[stderr_id] = behavior::inherit(STDERR_FILENO); +#elif defined(BOOST_WINDOWS_API) + streams[stdin_id] = behavior::inherit(GetStdHandle(STD_INPUT_HANDLE)); + streams[stdout_id] = behavior::inherit(GetStdHandle(STD_OUTPUT_HANDLE)); + streams[stderr_id] = behavior::inherit(GetStdHandle(STD_ERROR_HANDLE)); +#endif + } + +#if defined(BOOST_POSIX_API) || defined(BOOST_PROCESS_DOXYGEN) + /** + * Setups a child process. + * + * This is an extension point to support more configuration options for + * child processes. You can initialize \a setup with a user-defined function + * which is called when a child process is created. + * + * On POSIX platforms setup() is called in the child process. That's why in + * a multithreaded application only async-signal-safe functions must be + * called in the function \a setup is bound to. + * + * On Windows platforms setup() is called in the parent process. A + * reference to a STARTUPINFOA structure is passed as parameter. + */ + boost::function<void ()> setup; +#elif defined(BOOST_WINDOWS_API) + boost::function<void (STARTUPINFOA&)> setup; +#endif +}; + +} +} + +#endif
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/third_party/boost/process/detail/basic_status.hpp Thu Aug 25 11:05:55 2011 +0100 @@ -0,0 +1,69 @@ +// +// Boost.Process +// ~~~~~~~~~~~~~ +// +// Copyright (c) 2006, 2007 Julio M. Merino Vidal +// Copyright (c) 2008 Ilya Sokolov, Boris Schaeling +// Copyright (c) 2009 Boris Schaeling +// Copyright (c) 2010 Felipe Tanus, Boris Schaeling +// +// Distributed under the Boost Software License, Version 1.0. (See accompanying +// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) +// + +/** + * \file boost/process/detail/basic_status.hpp + * + * Includes the declaration of the basic status class. + */ + +#ifndef BOOST_PROCESS_DETAIL_BASIC_STATUS_HPP +#define BOOST_PROCESS_DETAIL_BASIC_STATUS_HPP + +#include <boost/process/config.hpp> +#include <boost/process/pid_type.hpp> +#include <boost/asio.hpp> + +namespace boost { +namespace process { +namespace detail { + +/** + * The basic_status class to wait for processes to exit. + * + * The basic_status class is a Boost.Asio I/O object and supports synchronous + * and asynchronous wait operations. It must be instantiated with a Service. + */ +template <typename Service> +class basic_status + : public boost::asio::basic_io_object<Service> +{ +public: + explicit basic_status(boost::asio::io_service &io_service) + : boost::asio::basic_io_object<Service>(io_service) + { + } + + /** + * Waits synchronously for a process to exit. + */ + int wait(pid_type pid) + { + return this->service.wait(this->implementation, pid); + } + + /** + * Waits asynchronously for a process to exit. + */ + template <typename Handler> + void async_wait(pid_type pid, Handler handler) + { + this->service.async_wait(this->implementation, pid, handler); + } +}; + +} +} +} + +#endif
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/third_party/boost/process/detail/basic_status_service.hpp Thu Aug 25 11:05:55 2011 +0100 @@ -0,0 +1,318 @@ +// +// Boost.Process +// ~~~~~~~~~~~~~ +// +// Copyright (c) 2006, 2007 Julio M. Merino Vidal +// Copyright (c) 2008 Ilya Sokolov, Boris Schaeling +// Copyright (c) 2009 Boris Schaeling +// Copyright (c) 2010 Felipe Tanus, Boris Schaeling +// +// Distributed under the Boost Software License, Version 1.0. (See accompanying +// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) +// + +/** + * \file boost/process/detail/basic_status_service.hpp + * + * Includes the declaration of the basic status service class. + */ + +#ifndef BOOST_PROCESS_DETAIL_BASIC_STATUS_SERVICE_HPP +#define BOOST_PROCESS_DETAIL_BASIC_STATUS_SERVICE_HPP + +#include <boost/process/config.hpp> + +#if defined(BOOST_POSIX_API) +# include <boost/process/operations.hpp> +# include <string> +# include <sys/types.h> +# include <sys/wait.h> +#elif defined(BOOST_WINDOWS_API) +# include <windows.h> +#else +# error "Unsupported platform." +#endif + +#include <boost/process/pid_type.hpp> +#include <boost/process/detail/status_impl.hpp> +#include <boost/asio.hpp> +#include <boost/thread.hpp> +#include <boost/shared_ptr.hpp> +#include <boost/make_shared.hpp> +#include <boost/scoped_ptr.hpp> +#include <boost/system/error_code.hpp> +#include <boost/unordered_map.hpp> +#include <vector> +#include <algorithm> + +namespace boost { +namespace process { +namespace detail { + +/** + * The basic_status_service class provides the service to wait for processes + * synchronously and asynchronously. + */ +template <typename StatusImplementation = status_impl> +class basic_status_service + : public boost::asio::detail::service_base<StatusImplementation> +{ +public: + explicit basic_status_service(boost::asio::io_service &io_service) + : boost::asio::detail::service_base<StatusImplementation>(io_service), +#if defined(BOOST_POSIX_API) + interrupt_pid_(-1), + pids_(0) +#elif defined(BOOST_WINDOWS_API) + run_(true) +#endif + { +#if defined(BOOST_WINDOWS_API) + handles_.push_back(CreateEvent(NULL, FALSE, FALSE, NULL)); + if (handles_[0] == NULL) + BOOST_PROCESS_THROW_LAST_SYSTEM_ERROR("CreateEvent() failed"); + work_thread_ = boost::thread( + &basic_status_service<StatusImplementation>::work_thread, this); +#endif + } + + ~basic_status_service() + { +#if defined(BOOST_POSIX_API) + boost::unique_lock<boost::mutex> lock(work_thread_mutex_); + bool worker_thread_active = (pids_ != 0); + lock.unlock(); + if (worker_thread_active) + { + stop_work_thread(); + work_thread_.join(); + } +#elif defined(BOOST_WINDOWS_API) + stop_work_thread(); + work_thread_.join(); + CloseHandle(handles_[0]); +#endif + } + + typedef boost::shared_ptr<StatusImplementation> implementation_type; + + void construct(implementation_type &impl) + { + impl = boost::make_shared<StatusImplementation>(); + boost::unique_lock<boost::mutex> lock(work_thread_mutex_); + impls_.push_back(impl); + } + + void destroy(implementation_type &impl) + { + boost::unique_lock<boost::mutex> lock(work_thread_mutex_); + typename std::vector<implementation_type>::iterator it = + std::find(impls_.begin(), impls_.end(), impl); + if (it != impls_.end()) + impls_.erase(it); +#if defined(BOOST_WINDOWS_API) + interrupt_work_thread(); + work_thread_cond_.wait(work_thread_mutex_); + impl->clear(handles_); + work_thread_cond_.notify_all(); +#endif + impl.reset(); + } + + int wait(implementation_type &impl, pid_type pid) + { + boost::system::error_code ec; + int status = impl->wait(pid, ec); +#if defined(BOOST_POSIX_API) + if (ec.value() == ECHILD) + { + boost::unique_lock<boost::mutex> lock(work_thread_mutex_); + boost::unordered_map<pid_t, int>::iterator it = statuses_.find(pid); + if (it == statuses_.end()) + { + work_thread_cond_.wait(work_thread_mutex_); + it = statuses_.find(pid); + } + if (it != statuses_.end()) + { + status = it->second; + statuses_.erase(it); + ec.clear(); + } + } +#endif + boost::asio::detail::throw_error(ec); + return status; + } + + template <typename Handler> + void async_wait(implementation_type &impl, pid_type pid, Handler handler) + { +#if defined(BOOST_POSIX_API) + boost::unique_lock<boost::mutex> lock(work_thread_mutex_); + if (++pids_ == 1) + { + work_.reset(new boost::asio::io_service::work( + this->get_io_service())); + work_thread_ = boost::thread( + &basic_status_service<StatusImplementation>::work_thread, + this); + } + impl->async_wait(pid, this->get_io_service().wrap(handler)); +#elif defined(BOOST_WINDOWS_API) + HANDLE handle = OpenProcess(SYNCHRONIZE | PROCESS_QUERY_INFORMATION, + FALSE, pid); + if (handle == NULL) + BOOST_PROCESS_THROW_LAST_SYSTEM_ERROR("OpenProcess() failed"); + boost::unique_lock<boost::mutex> lock(work_thread_mutex_); + if (!work_) + work_.reset(new boost::asio::io_service::work( + this->get_io_service())); + interrupt_work_thread(); + work_thread_cond_.wait(work_thread_mutex_); + handles_.push_back(handle); + impl->async_wait(handle, this->get_io_service().wrap(handler)); + work_thread_cond_.notify_all(); +#endif + } + +private: + void shutdown_service() + { +#if defined(BOOST_WINDOWS_API) + boost::unique_lock<boost::mutex> lock(work_thread_mutex_); + work_.reset(); +#endif + } + + void work_thread() + { +#if defined(BOOST_POSIX_API) + for (;;) + { + int status; + pid_t pid = ::wait(&status); + if (pid == -1) + { + if (errno != EINTR) + BOOST_PROCESS_THROW_LAST_SYSTEM_ERROR("wait(2) failed"); + } + else if (interrupted(pid)) + { + // On POSIX the only reason to interrupt is to break out. + break; + } + else + { + boost::unique_lock<boost::mutex> lock(work_thread_mutex_); + bool regchild = false; + for (typename std::vector<implementation_type>::iterator it = + impls_.begin(); it != impls_.end(); ++it) + regchild |= (*it)->complete(pid, status); + if (regchild && --pids_ == 0) + { + work_.reset(); + break; + } + else if (!regchild) + { + statuses_.insert(boost::unordered_map<pid_t, int>:: + value_type(pid, status)); + work_thread_cond_.notify_all(); + } + } + } +#elif defined(BOOST_WINDOWS_API) + for (;;) + { + DWORD res = WaitForMultipleObjects(handles_.size(), &handles_[0], + FALSE, INFINITE); + if (res == WAIT_FAILED) + BOOST_PROCESS_THROW_LAST_SYSTEM_ERROR( + "WaitForMultipleObjects() failed"); + else if (res - WAIT_OBJECT_0 == 0) + { + boost::unique_lock<boost::mutex> lock(work_thread_mutex_); + if (!run_) + break; + work_thread_cond_.notify_all(); + work_thread_cond_.wait(work_thread_mutex_); + } + else if (res - WAIT_OBJECT_0 > 0) + { + HANDLE handle = handles_[res - WAIT_OBJECT_0]; + DWORD exit_code; + if (!GetExitCodeProcess(handle, &exit_code)) + BOOST_PROCESS_THROW_LAST_SYSTEM_ERROR( + "GetExitCodeProcess() failed"); + boost::unique_lock<boost::mutex> lock(work_thread_mutex_); + for (typename std::vector<implementation_type>::iterator it = + impls_.begin(); it != impls_.end(); ++it) + (*it)->complete(handle, exit_code); + std::vector<HANDLE>::iterator it = handles_.begin(); + std::advance(it, res - WAIT_OBJECT_0); + handles_.erase(it); + if (handles_.size() == 1) + work_.reset(); + } + } +#endif + } + + void interrupt_work_thread() + { +#if defined(BOOST_POSIX_API) + // By creating a child process which immediately exits + // we interrupt wait(). + std::vector<std::string> args; + args.push_back("-c"); + args.push_back("'exit'"); + interrupt_pid_ = create_child("/bin/sh", args).get_id(); +#elif defined(BOOST_WINDOWS_API) + // By signaling the event in the first slot WaitForMultipleObjects() + // will return. The work thread won't do anything except checking if + // it should continue to run. + if (!SetEvent(handles_[0])) + BOOST_PROCESS_THROW_LAST_SYSTEM_ERROR("SetEvent() failed"); +#endif + } + +#if defined(BOOST_POSIX_API) + bool interrupted(pid_t pid) + { + boost::mutex::scoped_lock lock(work_thread_mutex_); + return interrupt_pid_ == pid; + } +#endif + + void stop_work_thread() + { + boost::mutex::scoped_lock lock(work_thread_mutex_); +#if defined(BOOST_WINDOWS_API) + // Access to run_ must be sychronized with running(). + run_ = false; +#endif + // Access to interrupt_pid_ must be sychronized with interrupted(). + interrupt_work_thread(); + } + + boost::scoped_ptr<boost::asio::io_service::work> work_; + std::vector<implementation_type> impls_; + boost::mutex work_thread_mutex_; + boost::thread work_thread_; + boost::condition_variable_any work_thread_cond_; +#if defined(BOOST_POSIX_API) + pid_t interrupt_pid_; + int pids_; + boost::unordered_map<pid_t, int> statuses_; +#elif defined(BOOST_WINDOWS_API) + bool run_; + std::vector<HANDLE> handles_; +#endif +}; + +} +} +} + +#endif
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/third_party/boost/process/detail/posix_helpers.hpp Thu Aug 25 11:05:55 2011 +0100 @@ -0,0 +1,106 @@ +// +// Boost.Process +// ~~~~~~~~~~~~~ +// +// Copyright (c) 2006, 2007 Julio M. Merino Vidal +// Copyright (c) 2008 Ilya Sokolov, Boris Schaeling +// Copyright (c) 2009 Boris Schaeling +// Copyright (c) 2010 Felipe Tanus, Boris Schaeling +// +// Distributed under the Boost Software License, Version 1.0. (See accompanying +// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) +// + +/** + * \file boost/process/detail/posix_helpers.hpp + * + * Includes the declaration of helper functions for POSIX systems. + */ + +#ifndef BOOST_PROCESS_POSIX_HELPERS_HPP +#define BOOST_PROCESS_POSIX_HELPERS_HPP + +#include <boost/process/config.hpp> +#include <boost/process/environment.hpp> +#include <string> +#include <utility> +#include <cstring> +#include <cstddef> + +namespace boost { +namespace process { +namespace detail { + +/** + * Converts an environment to a char** table as used by execve(). + * + * Converts the environment's contents to the format used by the + * execve() system call. The returned char** array is allocated + * in dynamic memory; the caller must free it when not used any + * more. Each entry is also allocated in dynamic memory and is a + * NULL-terminated string of the form var=value; these must also be + * released by the caller. + * + * This operation is only available on POSIX systems. + * + * \return The first argument of the pair is an integer that indicates + * how many strings are stored in the second argument. The + * second argument is a NULL-terminated, dynamically allocated + * array of dynamically allocated strings representing the + * enviroment's content. Each array entry is a NULL-terminated + * string of the form var=value. The caller is responsible for + * freeing them. + */ +inline std::pair<std::size_t, char**> environment_to_envp(const environment + &env) +{ + std::size_t nargs = env.size(); + char **envp = new char*[nargs + 1]; + environment::size_type i = 0; + for (environment::const_iterator it = env.begin(); it != env.end(); ++it) + { + std::string s = it->first + "=" + it->second; + envp[i] = new char[s.size() + 1]; + std::strncpy(envp[i], s.c_str(), s.size() + 1); + ++i; + } + envp[i] = 0; + return std::pair<std::size_t, char**>(nargs, envp); +} + +/** + * Converts the command line to an array of C strings. + * + * Converts the command line's list of arguments to the format expected + * by the \a argv parameter in the POSIX execve() system call. + * + * This operation is only available on POSIX systems. + * + * \return The first argument of the pair is an integer that indicates + * how many strings are stored in the second argument. The + * second argument is a NULL-terminated, dynamically allocated + * array of dynamically allocated strings holding the arguments + * to the executable. The caller is responsible for freeing them. + */ +template <class Arguments> +inline std::pair<std::size_t, char**> collection_to_argv(const Arguments &args) +{ + std::size_t nargs = args.size(); + char **argv = new char*[nargs + 1]; + typename Arguments::size_type i = 0; + for (typename Arguments::const_iterator it = args.begin(); it != args.end(); + ++it) + { + argv[i] = new char[it->size() + 1]; + std::strncpy(argv[i], it->c_str(), it->size() + 1); + ++i; + } + argv[nargs] = 0; + return std::pair<std::size_t, char**>(nargs, argv); +} + +} +} +} + +#endif
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/third_party/boost/process/detail/status_impl.hpp Thu Aug 25 11:05:55 2011 +0100 @@ -0,0 +1,189 @@ +// +// Boost.Process +// ~~~~~~~~~~~~~ +// +// Copyright (c) 2006, 2007 Julio M. Merino Vidal +// Copyright (c) 2008 Ilya Sokolov, Boris Schaeling +// Copyright (c) 2009 Boris Schaeling +// Copyright (c) 2010 Felipe Tanus, Boris Schaeling +// +// Distributed under the Boost Software License, Version 1.0. (See accompanying +// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) +// + +/** + * \file boost/process/detail/status_impl.hpp + * + * Includes the declaration of the status implementation class. + */ + +#ifndef BOOST_PROCESS_DETAIL_STATUS_IMPL_HPP +#define BOOST_PROCESS_DETAIL_STATUS_IMPL_HPP + +#include <boost/process/config.hpp> + +#if defined(BOOST_POSIX_API) +# include <sys/types.h> +# include <signal.h> +# include <sys/wait.h> +# include <errno.h> +#elif defined(BOOST_WINDOWS_API) +# include <windows.h> +#else +# error "Unsupported platform." +#endif + +#include <boost/process/pid_type.hpp> +#include <boost/system/error_code.hpp> +#include <boost/ptr_container/ptr_unordered_map.hpp> +#include <algorithm> + +namespace boost { +namespace process { +namespace detail { + +#if defined(BOOST_POSIX_API) +typedef pid_t phandle; +#elif defined(BOOST_WINDOWS_API) +typedef HANDLE phandle; +#endif + +struct operation +{ + virtual void operator()(int exit_code) + { +#if defined(BOOST_MSVC) + exit_code; +#endif + } +}; + +template <typename Handler> +class wrapped_handler : public operation +{ +public: + wrapped_handler(Handler handler) + : handler_(handler) + { + } + + void operator()(int exit_code) + { + handler_(boost::system::error_code(), exit_code); + } + +private: + Handler handler_; +}; + +/** + * The status_impl class saves internal data of every status I/O object. + */ +class status_impl +{ +public: +#if defined(BOOST_WINDOWS_API) + template <typename Container> + void clear(Container &handles) + { + for (operations_type::iterator it = ops_.begin(); it != ops_.end(); + ++it) + { + for (typename Container::iterator it2 = handles.begin(); it2 != + handles.end(); ++it2) + { + if (*it2 == it->first) + { + handles.erase(it2); + break; + } + } + CloseHandle(it->first); + } + } +#endif + + int wait(pid_type pid, boost::system::error_code &ec) + { +#if defined(BOOST_POSIX_API) + pid_t p; + int status; + do + { + p = waitpid(pid, &status, 0); + } while (p == -1 && errno == EINTR); + if (p == -1) + { + ec = boost::system::error_code(errno, + boost::system::get_system_category()); + return -1; + } + return status; +#elif defined(BOOST_WINDOWS_API) + HANDLE h = OpenProcess(PROCESS_QUERY_INFORMATION | SYNCHRONIZE, FALSE, + pid); + if (h == NULL) + { + ec = boost::system::error_code(GetLastError(), + boost::system::get_system_category()); + return -1; + } + + if (WaitForSingleObject(h, INFINITE) == WAIT_FAILED) + { + CloseHandle(h); + ec = boost::system::error_code(GetLastError(), + boost::system::get_system_category()); + return -1; + } + + DWORD exit_code; + if (!GetExitCodeProcess(h, &exit_code)) + { + CloseHandle(h); + ec = boost::system::error_code(GetLastError(), + boost::system::get_system_category()); + return -1; + } + if (!CloseHandle(h)) + { + ec = boost::system::error_code(GetLastError(), + boost::system::get_system_category()); + return -1; + } + return exit_code; +#endif + } + + template <typename Handler> + void async_wait(phandle ph, Handler handler) + { + ops_.insert(ph, new wrapped_handler<Handler>(handler)); + } + + bool complete(phandle ph, int exit_code) + { + boost::iterator_range<operations_type::iterator> r = + ops_.equal_range(ph); + if (r.empty()) + return false; + for (operations_type::iterator it = r.begin(); it != r.end(); ++it) + (*it->second)(exit_code); + ops_.erase(r.begin(), r.end()); +#if defined(BOOST_WINDOWS_API) + if (!CloseHandle(ph)) + BOOST_PROCESS_THROW_LAST_SYSTEM_ERROR("CloseHandle() failed"); +#endif + return true; + } + +private: + typedef boost::ptr_unordered_multimap<phandle, operation> operations_type; + operations_type ops_; +}; + +} +} +} + +#endif
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/third_party/boost/process/detail/systembuf.hpp Thu Aug 25 11:05:55 2011 +0100 @@ -0,0 +1,228 @@ +// +// Boost.Process +// ~~~~~~~~~~~~~ +// +// Copyright (c) 2006, 2007 Julio M. Merino Vidal +// Copyright (c) 2008 Ilya Sokolov, Boris Schaeling +// Copyright (c) 2009 Boris Schaeling +// Copyright (c) 2010 Felipe Tanus, Boris Schaeling +// +// Distributed under the Boost Software License, Version 1.0. (See accompanying +// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) +// + +/** + * \file boost/process/detail/systembuf.hpp + * + * Includes the declaration of the systembuf class. + */ + +#ifndef BOOST_PROCESS_DETAIL_SYSTEMBUF_HPP +#define BOOST_PROCESS_DETAIL_SYSTEMBUF_HPP + +#include <boost/process/config.hpp> + +#if defined(BOOST_POSIX_API) +# include <sys/types.h> +# include <unistd.h> +#elif defined(BOOST_WINDOWS_API) +# include <windows.h> +#else +# error "Unsupported platform." +#endif + +#include <boost/noncopyable.hpp> +#include <boost/scoped_array.hpp> +#include <boost/assert.hpp> +#include <streambuf> +#include <cstddef> + +namespace boost { +namespace process { + +class postream; + +namespace detail { + +/** + * std::streambuf implementation for handles. + * + * systembuf provides a std::streambuf implementation for handles. + * Contrarywise to the handle class, this class does \b not take + * ownership of the native handle; this should be taken care of + * somewhere else. + * + * This class follows the expected semantics of a std::streambuf object. + * However, it is not copyable to avoid introducing inconsistences with + * the on-disk file and the in-memory buffers. + */ +class systembuf : public std::streambuf, public boost::noncopyable +{ + friend class boost::process::postream; + +public: +#if defined(BOOST_PROCESS_DOXYGEN) + /** + * Opaque name for the native handle type. + */ + typedef NativeHandleType handle_type; +#elif defined(BOOST_POSIX_API) + typedef int handle_type; +#elif defined(BOOST_WINDOWS_API) + typedef HANDLE handle_type; +#endif + + /** + * Constructs a new systembuf for the given handle. + * + * This constructor creates a new systembuf object that reads or + * writes data from/to the \a h native handle. This handle + * is \b not owned by the created systembuf object; the code + * should take care of it externally. + * + * This class buffers input and output; the buffer size may be + * tuned through the \a bufsize parameter, which defaults to 8192 + * bytes. + * + * \see pistream and postream + */ + explicit systembuf(handle_type h, std::size_t bufsize = 8192) + : handle_(h), + bufsize_(bufsize), + read_buf_(new char[bufsize]), + write_buf_(new char[bufsize]) + { +#if defined(BOOST_POSIX_API) + BOOST_ASSERT(handle_ >= 0); +#elif defined(BOOST_WINDOWS_API) + BOOST_ASSERT(handle_ != INVALID_HANDLE_VALUE); +#endif + BOOST_ASSERT(bufsize_ > 0); + setp(write_buf_.get(), write_buf_.get() + bufsize_); + } + +protected: + /** + * Reads new data from the native handle. + * + * This operation is called by input methods when there is no more + * data in the input buffer. The function fills the buffer with new + * data, if available. + * + * \pre All input positions are exhausted (gptr() >= egptr()). + * \post The input buffer has new data, if available. + * \returns traits_type::eof() if a read error occurrs or there are + * no more data to be read. Otherwise returns + * traits_type::to_int_type(*gptr()). + */ + virtual int_type underflow() + { + BOOST_ASSERT(gptr() >= egptr()); + + bool ok; +#if defined(BOOST_POSIX_API) + ssize_t cnt = read(handle_, read_buf_.get(), bufsize_); + ok = (cnt != -1 && cnt != 0); +#elif defined(BOOST_WINDOWS_API) + DWORD cnt; + BOOL res = ReadFile(handle_, read_buf_.get(), bufsize_, &cnt, NULL); + ok = (res && cnt > 0); +#endif + + if (!ok) + return traits_type::eof(); + else + { + setg(read_buf_.get(), read_buf_.get(), read_buf_.get() + cnt); + return traits_type::to_int_type(*gptr()); + } + } + + /** + * Makes room in the write buffer for additional data. + * + * This operation is called by output methods when there is no more + * space in the output buffer to hold a new element. The function + * first flushes the buffer's contents to disk and then clears it to + * leave room for more characters. The given \a c character is + * stored at the beginning of the new space. + * + * \pre All output positions are exhausted (pptr() >= epptr()). + * \post The output buffer has more space if no errors occurred + * during the write to disk. + * \post *(pptr() - 1) is \a c. + * \returns traits_type::eof() if a write error occurrs. Otherwise + * returns traits_type::not_eof(c). + */ + virtual int_type overflow(int c) + { + BOOST_ASSERT(pptr() >= epptr()); + + if (sync() == -1) + return traits_type::eof(); + + if (!traits_type::eq_int_type(c, traits_type::eof())) + { + traits_type::assign(*pptr(), static_cast<traits_type::char_type>( + c)); + pbump(1); + } + + return traits_type::not_eof(c); + } + + /** + * Flushes the output buffer to disk. + * + * Synchronizes the systembuf buffers with the contents of the file + * associated to this object through the native handle. The output buffer + * is flushed to disk and cleared to leave new room for more data. + * + * \returns 0 on success, -1 if an error occurred. + */ + virtual int sync() + { +#if defined(BOOST_POSIX_API) + ssize_t cnt = pptr() - pbase(); + bool ok = (write(handle_, pbase(), cnt) == cnt); + if (ok) + pbump(static_cast<int>(-cnt)); + return ok ? 0 : -1; +#elif defined(BOOST_WINDOWS_API) + long cnt = pptr() - pbase(); + DWORD rcnt; + BOOL res = WriteFile(handle_, pbase(), cnt, &rcnt, NULL); + bool ok = (res && static_cast<long>(rcnt) == cnt); + if (ok) + pbump(-cnt); + return ok ? 0 : -1; +#endif + } + +private: + /** + * Native handle used by the systembuf object. + */ + handle_type handle_; + + /** + * Internal buffer size used during read and write operations. + */ + std::size_t bufsize_; + + /** + * Internal buffer used during read operations. + */ + boost::scoped_array<char> read_buf_; + + /** + * Internal buffer used during write operations. + */ + boost::scoped_array<char> write_buf_; +}; + +} +} +} + +#endif
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/third_party/boost/process/detail/windows_helpers.hpp Thu Aug 25 11:05:55 2011 +0100 @@ -0,0 +1,138 @@ +// +// Boost.Process +// ~~~~~~~~~~~~~ +// +// Copyright (c) 2006, 2007 Julio M. Merino Vidal +// Copyright (c) 2008 Ilya Sokolov, Boris Schaeling +// Copyright (c) 2009 Boris Schaeling +// Copyright (c) 2010 Felipe Tanus, Boris Schaeling +// +// Distributed under the Boost Software License, Version 1.0. (See accompanying +// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) +// + +/** + * \file boost/process/detail/windows_helpers.hpp + * + * Includes the declaration of helper functions for Windows systems. + */ + +#ifndef BOOST_PROCESS_WINDOWS_HELPERS_HPP +#define BOOST_PROCESS_WINDOWS_HELPERS_HPP + +#include <boost/process/config.hpp> +#include <boost/process/environment.hpp> +#include <boost/shared_array.hpp> +#include <string> +#include <vector> +#include <cstddef> +#include <string.h> +#include <windows.h> + +namespace boost { +namespace process { +namespace detail { + +/** + * Converts an environment to a string used by CreateProcess(). + * + * Converts the environment's contents to the format used by the + * CreateProcess() system call. The returned char* string is + * allocated in dynamic memory; the caller must free it when not + * used any more. This is enforced by the use of a shared pointer. + * + * This operation is only available on Windows systems. + * + * \return A dynamically allocated char* string that represents + * the environment's content. This string is of the form + * var1=value1\\0var2=value2\\0\\0. + */ +inline boost::shared_array<char> environment_to_windows_strings(environment + &env) +{ + boost::shared_array<char> envp; + + if (env.empty()) + { + envp.reset(new char[2]); + ZeroMemory(envp.get(), 2); + } + else + { + std::string s; + for (environment::const_iterator it = env.begin(); it != env.end(); + ++it) + { + s += it->first + "=" + it->second; + s.push_back(0); + } + envp.reset(new char[s.size() + 1]); +#if (BOOST_MSVC >= 1400) + memcpy_s(envp.get(), s.size() + 1, s.c_str(), s.size() + 1); +#else + memcpy(envp.get(), s.c_str(), s.size() + 1); +#endif + } + + return envp; +} + +/** + * Converts the command line to a plain string. + * + * Converts the command line's list of arguments to the format expected by the + * \a lpCommandLine parameter in the CreateProcess() system call. + * + * This operation is only available on Windows systems. + * + * \return A dynamically allocated string holding the command line + * to be passed to the executable. It is returned in a + * shared_array object to ensure its release at some point. + */ +template <class Arguments> +inline boost::shared_array<char> collection_to_windows_cmdline(const Arguments + &args) +{ + typedef std::vector<std::string> arguments_t; + arguments_t args2; + typename Arguments::size_type i = 0; + std::size_t size = 0; + for (typename Arguments::const_iterator it = args.begin(); it != args.end(); + ++it) + { + std::string arg = *it; + + std::string::size_type pos = 0; + while ( (pos = arg.find('"', pos)) != std::string::npos) + { + arg.replace(pos, 1, "\\\""); + pos += 2; + } + + if (arg.find(' ') != std::string::npos) + arg = '\"' + arg + '\"'; + + if (i++ != args.size() - 1) + arg += ' '; + + args2.push_back(arg); + size += arg.size() + 1; + } + + boost::shared_array<char> cmdline(new char[size]); + cmdline.get()[0] = '\0'; + for (arguments_t::size_type i = 0; i < args.size(); ++i) +#if (BOOST_MSVC >= 1400) + strcat_s(cmdline.get(), size, args2[i].c_str()); +#else + strncat(cmdline.get(), args2[i].c_str(), args2[i].size()); +#endif + + return cmdline; +} + +} +} +} + +#endif
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/third_party/boost/process/environment.hpp Thu Aug 25 11:05:55 2011 +0100 @@ -0,0 +1,52 @@ +// +// Boost.Process +// ~~~~~~~~~~~~~ +// +// Copyright (c) 2006, 2007 Julio M. Merino Vidal +// Copyright (c) 2008 Ilya Sokolov, Boris Schaeling +// Copyright (c) 2009 Boris Schaeling +// Copyright (c) 2010 Felipe Tanus, Boris Schaeling +// +// Distributed under the Boost Software License, Version 1.0. (See accompanying +// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) +// + +/** + * \file boost/process/environment.hpp + * + * Includes the declaration of the environment class. + */ + +#ifndef BOOST_PROCESS_ENVIRONMENT_HPP +#define BOOST_PROCESS_ENVIRONMENT_HPP + +#include <map> +#include <string> + +namespace boost { +namespace process { + +/** + * Representation of a process' environment variables. + * + * The environment is a map that establishes an unidirectional + * association between variable names and their values and is + * represented by a string to string map. + * + * Variables may be defined to the empty string. Be aware that doing so + * is not portable: POSIX systems will treat such variables as being + * defined to the empty value, but Windows systems are not able to + * distinguish them from undefined variables. + * + * Neither POSIX nor Windows systems support a variable with no name. + * + * It is worthy to note that the environment is sorted alphabetically. + * This is provided for-free by the map container used to implement this + * type, and this behavior is required by Windows systems. + */ +typedef std::map<std::string, std::string> environment; + +} +} + +#endif
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/third_party/boost/process/handle.hpp Thu Aug 25 11:05:55 2011 +0100 @@ -0,0 +1,231 @@ +// +// Boost.Process +// ~~~~~~~~~~~~~ +// +// Copyright (c) 2006, 2007 Julio M. Merino Vidal +// Copyright (c) 2008 Ilya Sokolov, Boris Schaeling +// Copyright (c) 2009 Boris Schaeling +// Copyright (c) 2010 Felipe Tanus, Boris Schaeling +// +// Distributed under the Boost Software License, Version 1.0. (See accompanying +// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) +// + +/** + * \file boost/process/handle.hpp + * + * Includes the declaration of the handle class. + */ + +#ifndef BOOST_PROCESS_HANDLE_HPP +#define BOOST_PROCESS_HANDLE_HPP + +#include <boost/process/config.hpp> + +#if defined(BOOST_POSIX_API) +# include <unistd.h> +#elif defined(BOOST_WINDOWS_API) +# include <windows.h> +#else +# error "Unsupported platform." +#endif + +#include <boost/shared_ptr.hpp> +#include <boost/make_shared.hpp> + +namespace boost { +namespace process { + +/** + * RAII model for handles. + * + * The \a handle class is a RAII model for native handles. This class wraps + * one of such handles grabbing its ownership, and automaticaly closes it + * upon destruction. It is used to avoid leaking open handles, shall an + * unexpected execution trace occur. + */ +class handle +{ +public: +#if defined(BOOST_PROCESS_DOXYGEN) + /** + * Opaque name for the native handle type. + * + * On POSIX systems \a NativeSystemHandle is an integer type while it is + * a \a HANDLE on Windows systems. + */ + typedef NativeSystemHandle native_type; +#elif defined(BOOST_POSIX_API) + typedef int native_type; +#elif defined(BOOST_WINDOWS_API) + typedef HANDLE native_type; +#endif + + /** + * Constructs an invalid handle. + * + * \see valid() + */ + handle() + { + } + + /** + * RAII settings to specify if handle should be automatically closed. + */ + enum close_type { do_close, dont_close }; + + /** + * Constructs a handle from a native handle. + * + * This constructor creates a new \a handle object that takes + * ownership of the given \a native handle. If \a close is set to + * handle::dont_close the \a native handle is not closed upon destruction. + * The user must not close \a native if it is owned by a \a handle object. + * Ownership can be reclaimed using release(). + * + * \see release() + */ + handle(native_type native, close_type close = handle::do_close) + : impl_(boost::make_shared<impl>(native, close)) + { + } + + /** + * Checks whether the handle is valid or not. + * + * \return true if the handle is valid; false otherwise. + */ + bool valid() const + { + return impl_ && impl_->valid(); + } + + /** + * Closes the handle. + * + * \post The handle is invalid. + * \post The native handle is closed. + */ + void close() + { + if (impl_) + impl_->close(); + } + + /** + * Gets the native handle. + * + * The caller can issue any operation on it except closing it. + * If closing is required, release() shall be used. + * + * \return The native handle. + */ + native_type native() const + { + return impl_ ? impl_->native() : invalid_handle(); + } + + /** + * Reclaims ownership of the native handle. + * + * The caller is responsible of closing the native handle. + * + * \post The handle is invalid. + * \return The native handle. + */ + native_type release() + { + return impl_ ? impl_->release() : invalid_handle(); + } + +private: + class impl + { + public: + typedef handle::native_type native_type; + + impl(native_type native, close_type close) + : native_(native), + close_(close) + { + } + + ~impl() + { + if (valid() && close_ == handle::do_close) + { +#if defined(BOOST_POSIX_API) + ::close(native_); +#elif defined(BOOST_WINDOWS_API) + CloseHandle(native_); +#endif + } + } + + bool valid() const + { + return native_ != handle::invalid_handle(); + } + + void close() + { + if (valid()) + { +#if defined(BOOST_POSIX_API) + ::close(native_); +#elif defined(BOOST_WINDOWS_API) + CloseHandle(native_); +#endif + native_ = handle::invalid_handle(); + } + } + + native_type native() const + { + return native_; + } + + native_type release() + { + native_type native = native_; + native_ = handle::invalid_handle(); + return native; + } + + private: + native_type native_; + close_type close_; + }; + + /** + * Implementation of handle to store native handle value. + * + * A shared pointer is used as handles represent system resources. If a + * handle is closed and becomes invalid the state of copies of the handle + * object will be updated as they all share the handle implementation. + */ + boost::shared_ptr<impl> impl_; + + /** + * Constant function representing an invalid handle value. + * + * Returns the platform-specific handle value that represents an + * invalid handle. This is a constant function rather than a regular + * constant because, in the latter case, we cannot define it under + * Windows due to the value being of a complex type. + */ + static const native_type invalid_handle() + { +#if defined(BOOST_POSIX_API) + return -1; +#elif defined(BOOST_WINDOWS_API) + return INVALID_HANDLE_VALUE; +#endif + } +}; + +} +} + +#endif
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/third_party/boost/process/operations.hpp Thu Aug 25 11:05:55 2011 +0100 @@ -0,0 +1,432 @@ +// +// Boost.Process +// ~~~~~~~~~~~~~ +// +// Copyright (c) 2006, 2007 Julio M. Merino Vidal +// Copyright (c) 2008 Ilya Sokolov, Boris Schaeling +// Copyright (c) 2009 Boris Schaeling +// Copyright (c) 2010 Felipe Tanus, Boris Schaeling +// +// Distributed under the Boost Software License, Version 1.0. (See accompanying +// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) +// + +/** + * \file boost/process/operations.hpp + * + * Provides miscellaneous free functions. + */ + +#ifndef BOOST_PROCESS_OPERATIONS_HPP +#define BOOST_PROCESS_OPERATIONS_HPP + +#include <boost/process/config.hpp> + +#if defined(BOOST_POSIX_API) +# include <boost/process/detail/posix_helpers.hpp> +# include <utility> +# include <cstddef> +# include <stdlib.h> +# include <unistd.h> +# include <fcntl.h> +# if defined(__CYGWIN__) +# include <boost/scoped_array.hpp> +# include <sys/cygwin.h> +# endif +#elif defined(BOOST_WINDOWS_API) +# include <boost/process/detail/windows_helpers.hpp> +# include <boost/scoped_array.hpp> +# include <boost/shared_array.hpp> +# include <windows.h> +#else +# error "Unsupported platform." +#endif + +#include <boost/process/child.hpp> +#include <boost/process/context.hpp> +#include <boost/process/stream_id.hpp> +#include <boost/process/stream_ends.hpp> +#include <boost/process/handle.hpp> +#include <boost/filesystem/path.hpp> +#include <boost/algorithm/string/predicate.hpp> +#include <boost/system/system_error.hpp> +#include <boost/throw_exception.hpp> +#include <boost/assert.hpp> +#include <string> +#include <vector> +#include <map> +#include <utility> + +namespace boost { +namespace process { + +/** + * Locates the executable program \a file in all the directory components + * specified in \a path. If \a path is empty, the value of the PATH + * environment variable is used. + * + * The path variable is interpreted following the same conventions used + * to parse the PATH environment variable in the underlying platform. + * + * \throw boost::filesystem::filesystem_error If the file cannot be found + * in the path. + */ +inline std::string find_executable_in_path(const std::string &file, + std::string path = "") +{ +#if defined(BOOST_POSIX_API) + BOOST_ASSERT(file.find('/') == std::string::npos); +#elif defined(BOOST_WINDOWS_API) + BOOST_ASSERT(file.find_first_of("\\/") == std::string::npos); +#endif + + std::string result; + +#if defined(BOOST_POSIX_API) + if (path.empty()) + { + const char *envpath = getenv("PATH"); + if (!envpath) + boost::throw_exception(boost::filesystem::filesystem_error( + BOOST_PROCESS_SOURCE_LOCATION "file not found", file, + boost::system::errc::make_error_code( + boost::system::errc::no_such_file_or_directory))); + path = envpath; + } + BOOST_ASSERT(!path.empty()); + +#if defined(__CYGWIN__) + if (!cygwin_posix_path_list_p(path.c_str())) + { + int size = cygwin_win32_to_posix_path_list_buf_size(path.c_str()); + boost::scoped_array<char> cygpath(new char[size]); + cygwin_win32_to_posix_path_list(path.c_str(), cygpath.get()); + path = cygpath.get(); + } +#endif + + std::string::size_type pos1 = 0, pos2; + do + { + pos2 = path.find(':', pos1); + std::string dir = (pos2 != std::string::npos) ? + path.substr(pos1, pos2 - pos1) : path.substr(pos1); + std::string f = dir + + (boost::algorithm::ends_with(dir, "/") ? "" : "/") + file; + if (!access(f.c_str(), X_OK)) + result = f; + pos1 = pos2 + 1; + } while (pos2 != std::string::npos && result.empty()); +#elif defined(BOOST_WINDOWS_API) + const char *exts[] = { "", ".exe", ".com", ".bat", NULL }; + const char **ext = exts; + while (*ext) + { + char buf[MAX_PATH]; + char *dummy; + DWORD size = SearchPathA(path.empty() ? NULL : path.c_str(), + file.c_str(), *ext, MAX_PATH, buf, &dummy); + BOOST_ASSERT(size < MAX_PATH); + if (size > 0) + { + result = buf; + break; + } + ++ext; + } +#endif + + if (result.empty()) + boost::throw_exception(boost::filesystem::filesystem_error( + BOOST_PROCESS_SOURCE_LOCATION "file not found", file, + boost::system::errc::make_error_code( + boost::system::errc::no_such_file_or_directory))); + + return result; +} + +/** + * Extracts the program name from a given executable. + * + * \return The program name. On Windows the program name + * is returned without a file extension. + */ +inline std::string executable_to_progname(const std::string &exe) +{ + std::string::size_type begin = 0; + std::string::size_type end = std::string::npos; + +#if defined(BOOST_POSIX_API) + std::string::size_type slash = exe.rfind('/'); +#elif defined(BOOST_WINDOWS_API) + std::string::size_type slash = exe.find_last_of("/\\"); +#endif + if (slash != std::string::npos) + begin = slash + 1; + +#if defined(BOOST_WINDOWS_API) + if (exe.size() > 4 && (boost::algorithm::iends_with(exe, ".exe") || + boost::algorithm::iends_with(exe, ".com") || + boost::algorithm::iends_with(exe, ".bat"))) + end = exe.size() - 4; +#endif + + return exe.substr(begin, end - begin); +} + +/** + * Starts a new child process. + * + * Launches a new process based on the binary image specified by the + * executable, the set of arguments passed to it and the execution context. + * + * \remark Blocking remarks: This function may block if the device holding the + * executable blocks when loading the image. This might happen if, e.g., + * the binary is being loaded from a network share. + * + * \return A handle to the new child process. + */ +template <typename Arguments, typename Context> +inline child create_child(const std::string &executable, Arguments args, + Context ctx) +{ + typedef std::map<stream_id, stream_ends> handles_t; + handles_t handles; + typename Context::streams_t::iterator it = ctx.streams.begin(); + for (; it != ctx.streams.end(); ++it) + { + if (it->first == stdin_id) + handles[it->first] = it->second(input_stream); + else if (it->first == stdout_id) + handles[it->first] = it->second(output_stream); + else if (it->first == stderr_id) + handles[it->first] = it->second(output_stream); +#if defined(BOOST_POSIX_API) + else + handles[it->first] = it->second(unknown_stream); +#endif + } + + std::string p_name = ctx.process_name.empty() ? + executable_to_progname(executable) : ctx.process_name; + args.insert(args.begin(), p_name); + +#if defined(BOOST_POSIX_API) + // Between fork() and execve() only async-signal-safe functions + // must be called if multithreaded applications should be supported. + // That's why the following code is executed before fork() is called. +#if defined(F_MAXFD) + int maxdescs = fcntl(-1, F_MAXFD, 0); + if (maxdescs == -1) + maxdescs = sysconf(_SC_OPEN_MAX); +#else + int maxdescs = static_cast<int>(sysconf(_SC_OPEN_MAX)); +#endif + if (maxdescs == -1) + maxdescs = 1024; + std::vector<bool> closeflags(maxdescs, true); + std::pair<std::size_t, char**> argv = detail::collection_to_argv(args); + std::pair<std::size_t, char**> envp = + detail::environment_to_envp(ctx.env); + + const char *work_dir = ctx.work_dir.c_str(); + + pid_t pid = fork(); + if (pid == -1) + BOOST_PROCESS_THROW_LAST_SYSTEM_ERROR("fork(2) failed"); + else if (pid == 0) + { + if (chdir(work_dir) == -1) + { + write(STDERR_FILENO, "chdir() failed\n", 15); + _exit(127); + } + + for (handles_t::iterator it = handles.begin(); it != handles.end(); + ++it) + { + if (it->second.child.valid()) + { + handles_t::iterator it2 = it; + ++it2; + for (; it2 != handles.end(); ++it2) + { + if (it2->second.child.native() == it->first) + { + int fd = fcntl(it2->second.child.native(), F_DUPFD, + it->first + 1); + if (fd == -1) + { + write(STDERR_FILENO, "fcntl() failed\n", 15); + _exit(127); + } + it2->second.child = fd; + } + } + + if (dup2(it->second.child.native(), it->first) == -1) + { + write(STDERR_FILENO, "dup2() failed\n", 14); + _exit(127); + } + closeflags[it->first] = false; + } + } + + if (ctx.setup) + ctx.setup(); + + for (std::size_t i = 0; i < closeflags.size(); ++i) + { + if (closeflags[i]) + close(static_cast<int>(i)); + } + + execve(executable.c_str(), argv.second, envp.second); + + // Actually we should delete argv and envp data. As we must not + // call any non-async-signal-safe functions though we simply exit. + write(STDERR_FILENO, "execve() failed\n", 16); + _exit(127); + } + else + { + BOOST_ASSERT(pid > 0); + + for (std::size_t i = 0; i < argv.first; ++i) + delete[] argv.second[i]; + delete[] argv.second; + + for (std::size_t i = 0; i < envp.first; ++i) + delete[] envp.second[i]; + delete[] envp.second; + + std::map<stream_id, handle> parent_ends; + for (handles_t::iterator it = handles.begin(); it != handles.end(); + ++it) + parent_ends[it->first] = it->second.parent; + + return child(pid, parent_ends); + } +#elif defined(BOOST_WINDOWS_API) + STARTUPINFOA startup_info; + ZeroMemory(&startup_info, sizeof(startup_info)); + startup_info.cb = sizeof(startup_info); + startup_info.dwFlags |= STARTF_USESTDHANDLES; + startup_info.hStdInput = handles[stdin_id].child.native(); + startup_info.hStdOutput = handles[stdout_id].child.native(); + startup_info.hStdError = handles[stderr_id].child.native(); + + if (ctx.setup) + ctx.setup(startup_info); + + PROCESS_INFORMATION pi; + ZeroMemory(&pi, sizeof(pi)); + + boost::shared_array<char> cmdline = + detail::collection_to_windows_cmdline(args); + + boost::scoped_array<char> exe(new char[executable.size() + 1]); +#if (BOOST_MSVC >= 1400) + strcpy_s(exe.get(), executable.size() + 1, executable.c_str()); +#else + strcpy(exe.get(), executable.c_str()); +#endif + + boost::scoped_array<char> workdir(new char[ctx.work_dir.size() + 1]); +#if (BOOST_MSVC >= 1400) + strcpy_s(workdir.get(), ctx.work_dir.size() + 1, ctx.work_dir.c_str()); +#else + strcpy(workdir.get(), ctx.work_dir.c_str()); +#endif + + boost::shared_array<char> envstrs = + detail::environment_to_windows_strings(ctx.env); + + if (CreateProcessA(exe.get(), cmdline.get(), NULL, NULL, TRUE, 0, + envstrs.get(), workdir.get(), &startup_info, &pi) == 0) + BOOST_PROCESS_THROW_LAST_SYSTEM_ERROR("CreateProcess() failed"); + + handle hprocess(pi.hProcess); + + if (!CloseHandle(pi.hThread)) + BOOST_PROCESS_THROW_LAST_SYSTEM_ERROR("CloseHandle() failed"); + + std::map<stream_id, handle> parent_ends; + parent_ends[stdin_id] = handles[stdin_id].parent; + parent_ends[stdout_id] = handles[stdout_id].parent; + parent_ends[stderr_id] = handles[stderr_id].parent; + + return child(hprocess, parent_ends); +#endif +} + +/** + * \overload + */ +inline child create_child(const std::string &executable) +{ + return create_child(executable, std::vector<std::string>(), context()); +} + +/** + * \overload + */ +template <typename Arguments> +inline child create_child(const std::string &executable, Arguments args) +{ + return create_child(executable, args, context()); +} + +/** + * Starts a shell-based command. + * + * Executes the given command through the default system shell. The + * command is subject to pattern expansion, redirection and pipelining. + * The shell is launched as described by the parameters in the context. + * + * This function behaves similarly to the system(3) system call. In a + * POSIX system, the command is fed to /bin/sh whereas under a Windows + * system, it is fed to cmd.exe. It is difficult to write portable + * commands, but this function comes in handy in multiple situations. + * + * \remark Blocking remarks: This function may block if the device holding the + * executable blocks when loading the image. This might happen if, e.g., + * the binary is being loaded from a network share. + * + * \return A handle to the new child process. + */ +template <typename Context> +inline child shell(const std::string &command, Context ctx) +{ +#if defined(BOOST_POSIX_API) + std::string executable = "/bin/sh"; + std::vector<std::string> args; + args.push_back("-c"); + args.push_back(command); +#elif defined(BOOST_WINDOWS_API) + char sysdir[MAX_PATH]; + UINT size = GetSystemDirectoryA(sysdir, sizeof(sysdir)); + if (!size) + BOOST_PROCESS_THROW_LAST_SYSTEM_ERROR("GetSystemDirectory() failed"); + std::string executable = std::string(sysdir) + + (sysdir[size - 1] != '\\' ? "\\cmd.exe" : "cmd.exe"); + std::vector<std::string> args; + args.push_back("/c"); + args.push_back(command); +#endif + return create_child(executable, args, ctx); +} + +/** + * \overload + */ +inline child shell(const std::string &command) +{ + return shell(command, context()); +} + +} +} + +#endif
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/third_party/boost/process/pid_type.hpp Thu Aug 25 11:05:55 2011 +0100 @@ -0,0 +1,56 @@ +// +// Boost.Process +// ~~~~~~~~~~~~~ +// +// Copyright (c) 2006, 2007 Julio M. Merino Vidal +// Copyright (c) 2008 Ilya Sokolov, Boris Schaeling +// Copyright (c) 2009 Boris Schaeling +// Copyright (c) 2010 Felipe Tanus, Boris Schaeling +// +// Distributed under the Boost Software License, Version 1.0. (See accompanying +// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) +// + +/** + * \file boost/process/pid_type.hpp + * + * Includes the declaration of the pid type. + */ + +#ifndef BOOST_PROCESS_PID_TYPE_HPP +#define BOOST_PROCESS_PID_TYPE_HPP + +#include <boost/process/config.hpp> + +#if defined(BOOST_POSIX_API) +# include <sys/types.h> +#elif defined(BOOST_WINDOWS_API) +# include <windows.h> +#endif + +namespace boost { +namespace process { + +#if defined(BOOST_PROCESS_DOXYGEN) +/** + * Opaque name for the process identifier type. + * + * Each operating system identifies processes using a specific type. + * The \a pid_type type is used to transparently refer to a process + * regardless of the operating system. + * + * This type is guaranteed to be an integral type on all supported + * platforms. On POSIX systems it is defined as pid_t, on Windows systems as + * DWORD. + */ +typedef NativeProcessId pid_type; +#elif defined(BOOST_POSIX_API) +typedef pid_t pid_type; +#elif defined(BOOST_WINDOWS_API) +typedef DWORD pid_type; +#endif + +} +} + +#endif
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/third_party/boost/process/pipe.hpp Thu Aug 25 11:05:55 2011 +0100 @@ -0,0 +1,49 @@ +// +// Boost.Process +// ~~~~~~~~~~~~~ +// +// Copyright (c) 2006, 2007 Julio M. Merino Vidal +// Copyright (c) 2008 Ilya Sokolov, Boris Schaeling +// Copyright (c) 2009 Boris Schaeling +// Copyright (c) 2010 Felipe Tanus, Boris Schaeling +// +// Distributed under the Boost Software License, Version 1.0. (See accompanying +// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) +// + +/** + * \file boost/process/pipe.hpp + * + * Includes the declaration of the pipe class. + */ + +#ifndef BOOST_PROCESS_PIPE_HPP +#define BOOST_PROCESS_PIPE_HPP + +#include <boost/process/config.hpp> +#include <boost/asio.hpp> + +namespace boost { +namespace process { + +#if defined(BOOST_PROCESS_DOXYGEN) +/** + * The pipe class is a type definition for stream-based classes defined by + * Boost.Asio. + * + * The type definition is provided for convenience. You can also use Boost.Asio + * classes directly for asynchronous I/O operations. + */ +typedef BoostAsioPipe pipe; +#elif defined(BOOST_POSIX_API) +typedef boost::asio::posix::stream_descriptor pipe; +#elif defined(BOOST_WINDOWS_API) +typedef boost::asio::windows::stream_handle pipe; +#else +# error "Unsupported platform." +#endif + +} +} + +#endif
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/third_party/boost/process/pistream.hpp Thu Aug 25 11:05:55 2011 +0100 @@ -0,0 +1,114 @@ +// +// Boost.Process +// ~~~~~~~~~~~~~ +// +// Copyright (c) 2006, 2007 Julio M. Merino Vidal +// Copyright (c) 2008 Ilya Sokolov, Boris Schaeling +// Copyright (c) 2009 Boris Schaeling +// Copyright (c) 2010 Felipe Tanus, Boris Schaeling +// +// Distributed under the Boost Software License, Version 1.0. (See accompanying +// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) +// + +/** + * \file boost/process/pistream.hpp + * + * Includes the declaration of the pistream class. + */ + +#ifndef BOOST_PROCESS_PISTREAM_HPP +#define BOOST_PROCESS_PISTREAM_HPP + +#include <boost/process/handle.hpp> +#include <boost/process/detail/systembuf.hpp> +#include <boost/noncopyable.hpp> +#include <istream> + +namespace boost { +namespace process { + +/** + * Child process' output stream. + * + * The pistream class represents an output communication channel with the + * child process. The child process writes data to this stream and the + * parent process can read it through the pistream object. In other + * words, from the child's point of view, the communication channel is an + * output one, but from the parent's point of view it is an input one; + * hence the confusing pistream name. + * + * pistream objects cannot be copied because they buffer data + * that flows through the communication channel. + * + * A pistream object behaves as a std::istream stream in all senses. + * The class is only provided because it must provide a method to let + * the caller explicitly close the communication channel. + * + * \remark Blocking remarks: Functions that read data from this + * stream can block if the associated handle blocks during + * the read. As this class is used to communicate with child + * processes through anonymous pipes, the most typical blocking + * condition happens when the child has no more data to send to + * the pipe's system buffer. When this happens, the buffer + * eventually empties and the system blocks until the writer + * generates some data. + */ +class pistream : public std::istream, public boost::noncopyable +{ +public: + /** + * Creates a new process' output stream. + */ + explicit pistream(boost::process::handle h) + : std::istream(0), + handle_(h), + systembuf_(handle_.native()) + { + rdbuf(&systembuf_); + } + + /** + * Returns the handle managed by this stream. + */ + const boost::process::handle &handle() const + { + return handle_; + } + + /** + * Returns the handle managed by this stream. + */ + boost::process::handle &handle() + { + return handle_; + } + + /** + * Closes the handle managed by this stream. + * + * Explicitly closes the handle managed by this stream. This + * function can be used by the user to tell the child process it's + * not willing to receive more data. + */ + void close() + { + handle_.close(); + } + +private: + /** + * The handle managed by this stream. + */ + boost::process::handle handle_; + + /** + * The systembuf object used to manage this stream's data. + */ + detail::systembuf systembuf_; +}; + +} +} + +#endif
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/third_party/boost/process/postream.hpp Thu Aug 25 11:05:55 2011 +0100 @@ -0,0 +1,115 @@ +// +// Boost.Process +// ~~~~~~~~~~~~~ +// +// Copyright (c) 2006, 2007 Julio M. Merino Vidal +// Copyright (c) 2008 Ilya Sokolov, Boris Schaeling +// Copyright (c) 2009 Boris Schaeling +// Copyright (c) 2010 Felipe Tanus, Boris Schaeling +// +// Distributed under the Boost Software License, Version 1.0. (See accompanying +// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) +// + +/** + * \file boost/process/postream.hpp + * + * Includes the declaration of the postream class. + */ + +#ifndef BOOST_PROCESS_POSTREAM_HPP +#define BOOST_PROCESS_POSTREAM_HPP + +#include <boost/process/handle.hpp> +#include <boost/process/detail/systembuf.hpp> +#include <boost/noncopyable.hpp> +#include <ostream> + +namespace boost { +namespace process { + +/** + * Child process' input stream. + * + * The postream class represents an input communication channel with the + * child process. The child process reads data from this stream and the + * parent process can write to it through the postream object. In other + * words, from the child's point of view, the communication channel is an + * input one, but from the parent's point of view it is an output one; + * hence the confusing postream name. + * + * postream objects cannot be copied because they buffer data that flows + * through the communication channel. + * + * A postream object behaves as a std::ostream stream in all senses. + * The class is only provided because it must provide a method to let + * the caller explicitly close the communication channel. + * + * \remark Blocking remarks: Functions that write data to this + * stream can block if the associated handle blocks during + * the write. As this class is used to communicate with child + * processes through anonymous pipes, the most typical blocking + * condition happens when the child is not processing the data + * in the pipe's system buffer. When this happens, the buffer + * eventually fills up and the system blocks until the reader + * consumes some data, leaving some new room. + */ +class postream : public std::ostream, public boost::noncopyable +{ +public: + /** + * Creates a new process' input stream. + */ + explicit postream(boost::process::handle h) + : std::ostream(0), + handle_(h), + systembuf_(handle_.native()) + { + rdbuf(&systembuf_); + } + + /** + * Returns the handle managed by this stream. + */ + const boost::process::handle &handle() const + { + return handle_; + } + + /** + * Returns the handle managed by this stream. + */ + boost::process::handle &handle() + { + return handle_; + } + + /** + * Closes the handle managed by this stream. + * + * Explicitly closes the handle managed by this stream. This + * function can be used by the user to tell the child process there + * is no more data to send. + */ + void close() + { + systembuf_.sync(); + handle_.close(); + } + +private: + /** + * The handle managed by this stream. + */ + boost::process::handle handle_; + + /** + * The systembuf object used to manage this stream's data. + */ + detail::systembuf systembuf_; +}; + +} +} + +#endif
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/third_party/boost/process/process.hpp Thu Aug 25 11:05:55 2011 +0100 @@ -0,0 +1,212 @@ +// +// Boost.Process +// ~~~~~~~~~~~~~ +// +// Copyright (c) 2006, 2007 Julio M. Merino Vidal +// Copyright (c) 2008 Ilya Sokolov, Boris Schaeling +// Copyright (c) 2009 Boris Schaeling +// Copyright (c) 2010 Felipe Tanus, Boris Schaeling +// +// Distributed under the Boost Software License, Version 1.0. (See accompanying +// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) +// + +/** + * \file boost/process/process.hpp + * + * Includes the declaration of the process class. + */ + +#ifndef BOOST_PROCESS_PROCESS_HPP +#define BOOST_PROCESS_PROCESS_HPP + +#include <boost/process/config.hpp> + +#if defined(BOOST_POSIX_API) +# include <unistd.h> +# include <sys/types.h> +# include <signal.h> +# include <sys/wait.h> +# include <errno.h> +#elif defined(BOOST_WINDOWS_API) +# include <boost/process/handle.hpp> +# include <cstdlib> +# include <windows.h> +#else +# error "Unsupported platform." +#endif + +#include <boost/process/pid_type.hpp> + +namespace boost { +namespace process { + +/** + * Process class to represent any running process. + */ +class process +{ +public: + /** + * Constructs a new process object. + * + * Creates a new process object that represents a running process + * within the system. + * + * On Windows the process is opened and a handle saved. This is required + * to avoid the operating system removing process resources when the + * process exits. The handle is closed when the process instance (and all + * of its copies) is destroyed. + */ + process(pid_type id) + : id_(id) +#if defined(BOOST_WINDOWS_API) + , handle_(open_process(id)) +#endif + { + } + +#if defined(BOOST_WINDOWS_API) || defined(BOOST_PROCESS_DOXYGEN) + /** + * Constructs a new process object. + * + * Creates a new process object that represents a running process + * within the system. + * + * This operation is only available on Windows systems. The handle is + * closed when the process instance (and all of its copies) is destroyed. + */ + process(handle h) + : id_(GetProcessId(h.native())), + handle_(h) + { + } +#endif + + /** + * Returns the process identifier. + */ + pid_type get_id() const + { + return id_; + } + + /** + * Terminates the process execution. + * + * Forces the termination of the process execution. Some platforms + * allow processes to ignore some external termination notifications + * or to capture them for a proper exit cleanup. You can set the + * \a force flag to true to force their termination regardless + * of any exit handler. + * + * After this call, accessing this object can be dangerous because the + * process identifier may have been reused by a different process. It + * might still be valid, though, if the process has refused to die. + * + * \throw boost::system::system_error If system calls used to terminate the + * process fail. + */ + void terminate(bool force = false) const + { +#if defined(BOOST_POSIX_API) + if (kill(id_, force ? SIGKILL : SIGTERM) == -1) + BOOST_PROCESS_THROW_LAST_SYSTEM_ERROR("kill(2) failed"); +#elif defined(BOOST_WINDOWS_API) +#if defined(BOOST_MSVC) + force; +#endif + HANDLE h = OpenProcess(PROCESS_TERMINATE, FALSE, id_); + if (h == NULL) + BOOST_PROCESS_THROW_LAST_SYSTEM_ERROR("OpenProcess() failed"); + if (!TerminateProcess(h, EXIT_FAILURE)) + { + CloseHandle(h); + BOOST_PROCESS_THROW_LAST_SYSTEM_ERROR("TerminateProcess() failed"); + } + if (!CloseHandle(h)) + BOOST_PROCESS_THROW_LAST_SYSTEM_ERROR("CloseHandle() failed"); +#endif + } + + /** + * Blocks and waits for the process to terminate. + * + * Returns an exit code. The process object ceases to be valid after this + * call. + * + * \remark Blocking remarks: This call blocks if the process has not + * finalized execution and waits until it terminates. + * + * \throw boost::system::system_error If system calls used to wait for the + * process fail. + */ + int wait() const + { +#if defined(BOOST_POSIX_API) + pid_t p; + int status; + do + { + p = waitpid(id_, &status, 0); + } while (p == -1 && errno == EINTR); + if (p == -1) + BOOST_PROCESS_THROW_LAST_SYSTEM_ERROR("waitpid(2) failed"); + return status; +#elif defined(BOOST_WINDOWS_API) + HANDLE h = OpenProcess(PROCESS_QUERY_INFORMATION | SYNCHRONIZE, FALSE, + id_); + if (h == NULL) + BOOST_PROCESS_THROW_LAST_SYSTEM_ERROR("OpenProcess() failed"); + if (WaitForSingleObject(h, INFINITE) == WAIT_FAILED) + { + CloseHandle(h); + BOOST_PROCESS_THROW_LAST_SYSTEM_ERROR( + "WaitForSingleObject() failed"); + } + DWORD exit_code; + if (!GetExitCodeProcess(h, &exit_code)) + { + CloseHandle(h); + BOOST_PROCESS_THROW_LAST_SYSTEM_ERROR( + "GetExitCodeProcess() failed"); + } + if (!CloseHandle(h)) + BOOST_PROCESS_THROW_LAST_SYSTEM_ERROR("CloseHandle() failed"); + return exit_code; +#endif + } + +private: + /** + * The process identifier. + */ + pid_type id_; + +#if defined(BOOST_WINDOWS_API) + /** + * Opens a process and returns a handle. + * + * OpenProcess() returns NULL and not INVALID_HANDLE_VALUE on failure. + * That's why the return value is manually checked in this helper function + * instead of simply passing it to the constructor of the handle class. + */ + HANDLE open_process(pid_type id) + { + HANDLE h = OpenProcess(PROCESS_QUERY_INFORMATION, FALSE, id); + if (h == NULL) + BOOST_PROCESS_THROW_LAST_SYSTEM_ERROR("OpenProcess() failed"); + return h; + } + + /** + * The process handle. + */ + handle handle_; +#endif +}; + +} +} + +#endif
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/third_party/boost/process/self.hpp Thu Aug 25 11:05:55 2011 +0100 @@ -0,0 +1,188 @@ +// +// Boost.Process +// ~~~~~~~~~~~~~ +// +// Copyright (c) 2006, 2007 Julio M. Merino Vidal +// Copyright (c) 2008 Ilya Sokolov, Boris Schaeling +// Copyright (c) 2009 Boris Schaeling +// Copyright (c) 2010 Felipe Tanus, Boris Schaeling +// +// Distributed under the Boost Software License, Version 1.0. (See accompanying +// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) +// + +/** + * \file boost/process/self.hpp + * + * Includes the declaration of the self class. + */ + +#ifndef BOOST_PROCESS_SELF_HPP +#define BOOST_PROCESS_SELF_HPP + +#include <boost/process/config.hpp> + +#if defined(BOOST_POSIX_API) +# include <boost/scoped_array.hpp> +# include <errno.h> +# include <unistd.h> +# include <limits.h> +# if defined(__APPLE__) +# include <crt_externs.h> +# endif +#elif defined(BOOST_WINDOWS_API) +# include <windows.h> +#else +# error "Unsupported platform." +#endif + +#include <boost/process/process.hpp> +#include <boost/process/environment.hpp> +#include <boost/noncopyable.hpp> +#include <boost/assert.hpp> +#include <string> + +#if defined(BOOST_POSIX_API) +extern "C" +{ + extern char **environ; +} +#endif + +namespace boost { +namespace process { + +/** + * The self class provides access to the process itself. + */ +class self : public process, public boost::noncopyable +{ +public: + /** + * Returns the self instance representing the caller's process. + */ + static self &get_instance() + { + static self *instance = 0; + if (!instance) + instance = new self; + return *instance; + } + + /** + * Returns the current environment. + * + * Returns the current process environment variables. Modifying the + * returned object has no effect on the current environment. + */ + static environment get_environment() + { + environment e; + +#if defined(BOOST_POSIX_API) +# if defined(__APPLE__) + char **env = *_NSGetEnviron(); +# else + char **env = environ; +# endif + + while (*env) + { + std::string s = *env; + std::string::size_type pos = s.find('='); + e.insert(environment::value_type(s.substr(0, pos), + s.substr(pos + 1))); + ++env; + } +#elif defined(BOOST_WINDOWS_API) +# ifdef GetEnvironmentStrings +# undef GetEnvironmentStrings +# endif + + char *ms_environ = GetEnvironmentStrings(); + if (!ms_environ) + BOOST_PROCESS_THROW_LAST_SYSTEM_ERROR( + "GetEnvironmentStrings() failed"); + try + { + char *env = ms_environ; + while (*env) + { + std::string s = env; + std::string::size_type pos = s.find('='); + e.insert(environment::value_type(s.substr(0, pos), + s.substr(pos + 1))); + env += s.size() + 1; + } + } + catch (...) + { + FreeEnvironmentStringsA(ms_environ); + throw; + } + FreeEnvironmentStringsA(ms_environ); +#endif + + return e; + } + + /** + * Returns the current work directory. + */ + static std::string get_work_dir() + { +#if defined(BOOST_POSIX_API) +#if defined(PATH_MAX) + char buffer[PATH_MAX]; + char *cwd = buffer; + long size = PATH_MAX; +#elif defined(_PC_PATH_MAX) + errno = 0; + long size = pathconf("/", _PC_PATH_MAX); + if (size == -1 && errno) + BOOST_PROCESS_THROW_LAST_SYSTEM_ERROR("pathconf(2) failed"); + else if (size == -1) + size = BOOST_PROCESS_POSIX_PATH_MAX; + BOOST_ASSERT(size > 0); + boost::scoped_array<char> buffer(new char[size]); + char *cwd = buffer.get(); +#else + char buffer[BOOST_PROCESS_POSIX_PATH_MAX]; + char *cwd = buffer; + long size = BOOST_PROCESS_POSIX_PATH_MAX; +#endif + if (!getcwd(cwd, size)) + BOOST_PROCESS_THROW_LAST_SYSTEM_ERROR("getcwd(2) failed"); + BOOST_ASSERT(cwd[0] != '\0'); + return cwd; +#elif defined(BOOST_WINDOWS_API) + BOOST_ASSERT(MAX_PATH > 0); + char cwd[MAX_PATH]; + if (!GetCurrentDirectoryA(sizeof(cwd), cwd)) + BOOST_PROCESS_THROW_LAST_SYSTEM_ERROR( + "GetCurrentDirectory() failed"); + BOOST_ASSERT(cwd[0] != '\0'); + return cwd; +#endif + } + +private: + /** + * Constructs a new self object. + * + * Creates a new self object that represents the current process. + */ + self() : +#if defined(BOOST_POSIX_API) + process(getpid()) +#elif defined(BOOST_WINDOWS_API) + process(GetCurrentProcessId()) +#endif + { + } +}; + +} +} + +#endif
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/third_party/boost/process/status.hpp Thu Aug 25 11:05:55 2011 +0100 @@ -0,0 +1,41 @@ +// +// Boost.Process +// ~~~~~~~~~~~~~ +// +// Copyright (c) 2006, 2007 Julio M. Merino Vidal +// Copyright (c) 2008 Ilya Sokolov, Boris Schaeling +// Copyright (c) 2009 Boris Schaeling +// Copyright (c) 2010 Felipe Tanus, Boris Schaeling +// +// Distributed under the Boost Software License, Version 1.0. (See accompanying +// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) +// + +/** + * \file boost/process/status.hpp + * + * Includes the declaration of the status class. + */ + +#ifndef BOOST_PROCESS_STATUS_HPP +#define BOOST_PROCESS_STATUS_HPP + +#include <boost/process/config.hpp> +#include <boost/process/detail/basic_status.hpp> +#include <boost/process/detail/basic_status_service.hpp> + +namespace boost { +namespace process { + +/** + * The status class to wait for processes to exit. + * + * The status class is a Boost.Asio I/O object and supports synchronous + * and asynchronous wait operations. + */ +typedef detail::basic_status<detail::basic_status_service<> > status; + +} +} + +#endif
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/third_party/boost/process/stream_behavior.hpp Thu Aug 25 11:05:55 2011 +0100 @@ -0,0 +1,326 @@ +// +// Boost.Process +// ~~~~~~~~~~~~~ +// +// Copyright (c) 2006, 2007 Julio M. Merino Vidal +// Copyright (c) 2008 Ilya Sokolov, Boris Schaeling +// Copyright (c) 2009 Boris Schaeling +// Copyright (c) 2010 Felipe Tanus, Boris Schaeling +// +// Distributed under the Boost Software License, Version 1.0. (See accompanying +// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) +// + +/** + * \file boost/process/stream_behavior.hpp + * + * Includes the declaration of stream behavior classes. + */ + +#ifndef BOOST_PROCESS_STREAM_BEHAVIOR_HPP +#define BOOST_PROCESS_STREAM_BEHAVIOR_HPP + +#include <boost/process/config.hpp> + +#if defined(BOOST_POSIX_API) +# include <sys/stat.h> +# include <fcntl.h> +# include <unistd.h> +#elif defined(BOOST_WINDOWS_API) +# include <windows.h> +# include <rpc.h> +#endif + +#include <boost/process/stream_ends.hpp> +#include <boost/process/stream_type.hpp> +#include <boost/process/handle.hpp> +#include <string> +#include <algorithm> + +namespace boost { +namespace process { +namespace behavior { + +/** + * Stream behavior to close streams of a child process. + * + * A child process will not be able to use the stream. + */ +class close +{ +public: + stream_ends operator()(stream_type) const + { + return stream_ends(); + } +}; + +/** + * Stream behavior to make a child process inherit streams. + * + * A child process will use the very same stream of its parent process. + */ +class inherit +{ +public: + inherit(handle::native_type h) + : h_(h, handle::dont_close) + { +#if defined(BOOST_WINDOWS_API) + if (h != INVALID_HANDLE_VALUE) + { + if (!SetHandleInformation(h_.native(), HANDLE_FLAG_INHERIT, + HANDLE_FLAG_INHERIT)) + { + HANDLE proc = GetCurrentProcess(); + HANDLE dup; + if (!DuplicateHandle(proc, h_.native(), proc, &dup, 0, + TRUE, DUPLICATE_SAME_ACCESS)) + { + BOOST_PROCESS_THROW_LAST_SYSTEM_ERROR( + "DuplicateHandle() failed"); + } + h_ = dup; + } + } +#endif + } + + stream_ends operator()(stream_type) const + { + return stream_ends(h_, handle()); + } + +private: + handle h_; +}; + +/** + * Stream behavior to redirect streams with a pipe. + * + * A child process will be able to communicate with its parent process. + */ +class pipe +{ +public: +#if defined(BOOST_POSIX_API) + pipe() + : stype_(unknown_stream) + { + } + + pipe(stream_type stype) + : stype_(stype) + { + } +#endif + + stream_ends operator()(stream_type stype) const + { + handle::native_type ends[2]; +#if defined(BOOST_POSIX_API) + if (::pipe(ends) == -1) + BOOST_PROCESS_THROW_LAST_SYSTEM_ERROR("pipe(2) failed"); + if (stype_ != unknown_stream) + stype = stype_; +#elif defined(BOOST_WINDOWS_API) + SECURITY_ATTRIBUTES sa; + ZeroMemory(&sa, sizeof(sa)); + sa.nLength = sizeof(sa); + sa.lpSecurityDescriptor = NULL; + sa.bInheritHandle = FALSE; + if (!CreatePipe(&ends[0], &ends[1], &sa, 0)) + BOOST_PROCESS_THROW_LAST_SYSTEM_ERROR("CreatePipe() failed"); +#endif + handle child_end = ends[stype == input_stream ? 0 : 1]; + handle parent_end = ends[stype == input_stream ? 1 : 0]; +#if defined(BOOST_WINDOWS_API) + if (!SetHandleInformation(child_end.native(), HANDLE_FLAG_INHERIT, + HANDLE_FLAG_INHERIT)) + BOOST_PROCESS_THROW_LAST_SYSTEM_ERROR( + "SetHandleInformation() failed"); +#endif + return stream_ends(child_end, parent_end); + } + +#if defined(BOOST_POSIX_API) +private: + stream_type stype_; +#endif +}; + +/** + * Stream behavior to redirect streams with a named pipe. + * + * A child process will be able to communicate with its parent process. + */ +class named_pipe +{ +public: + named_pipe(const std::string &name) + : name_(name) +#if defined(BOOST_POSIX_API) + , stype_(unknown_stream) +#endif + { + } + +#if defined(BOOST_POSIX_API) + named_pipe(const std::string &name, stream_type stype) + : name_(name), + stype_(stype) + { + } +#endif + + stream_ends operator()(stream_type stype) const + { +#if defined(BOOST_POSIX_API) + if (mkfifo(name_.c_str(), S_IRUSR | S_IWUSR | S_IRGRP | S_IROTH) == -1) + BOOST_PROCESS_THROW_LAST_SYSTEM_ERROR("mkfifo(3) failed"); + handle child_end = open(name_.c_str(), O_RDONLY | O_NONBLOCK); + if (!child_end.valid()) + BOOST_PROCESS_THROW_LAST_SYSTEM_ERROR("open(2) failed"); + int opts = fcntl(child_end.native(), F_GETFL); + if (opts == -1) + BOOST_PROCESS_THROW_LAST_SYSTEM_ERROR("fcntl(2) failed"); + opts ^= O_NONBLOCK; + if (fcntl(child_end.native(), F_SETFL, opts) == -1) + BOOST_PROCESS_THROW_LAST_SYSTEM_ERROR("fcntl(2) failed"); + handle parent_end = open(name_.c_str(), O_WRONLY); + if (!parent_end.valid()) + BOOST_PROCESS_THROW_LAST_SYSTEM_ERROR("open(2) failed"); + if (stype_ != unknown_stream) + stype = stype_; +#elif defined(BOOST_WINDOWS_API) + SECURITY_ATTRIBUTES sa; + ZeroMemory(&sa, sizeof(sa)); + sa.nLength = sizeof(sa); + sa.lpSecurityDescriptor = NULL; + sa.bInheritHandle = TRUE; + handle child_end = CreateNamedPipeA(name_.c_str(), PIPE_ACCESS_INBOUND | + FILE_FLAG_OVERLAPPED, 0, 1, 8192, 8192, 0, &sa); + if (!child_end.valid()) + BOOST_PROCESS_THROW_LAST_SYSTEM_ERROR("CreateNamedPipe() failed"); + handle parent_end = CreateFileA(name_.c_str(), GENERIC_WRITE, 0, NULL, + OPEN_EXISTING, FILE_FLAG_OVERLAPPED, NULL); + if (!parent_end.valid()) + BOOST_PROCESS_THROW_LAST_SYSTEM_ERROR("CreateFile() failed"); +#endif + if (stype == output_stream) + std::swap(child_end, parent_end); +#if defined(BOOST_WINDOWS_API) + if (!SetHandleInformation(child_end.native(), HANDLE_FLAG_INHERIT, + HANDLE_FLAG_INHERIT)) + BOOST_PROCESS_THROW_LAST_SYSTEM_ERROR( + "SetHandleInformation() failed"); +#endif + return stream_ends(child_end, parent_end); + } + +private: + std::string name_; + +#if defined(BOOST_POSIX_API) + stream_type stype_; +#endif +}; + +/** + * Stream behavior to redirect streams with a pipe which supports asynchronous + * I/O. + * + * As platforms require different types of pipes for asynchronous I/O this + * stream behavior is provided for convenience. It uses the minimum required + * pipe type on a platform in order to be able to use asynchronous I/O. + */ +#if defined(BOOST_POSIX_API) || defined(BOOST_PROCESS_DOXYGEN) +typedef pipe async_pipe; +#elif defined(BOOST_WINDOWS_API) +class async_pipe +{ +public: + stream_ends operator()(stream_type stype) const + { + UUID uuid; + RPC_STATUS s = UuidCreateSequential(&uuid); + if (s != RPC_S_OK && s != RPC_S_UUID_LOCAL_ONLY) + BOOST_PROCESS_THROW_ERROR(s, "UuidCreateSequential() failed"); + unsigned char *c; + s = UuidToStringA(&uuid, &c); + if (s != RPC_S_OK) + BOOST_PROCESS_THROW_ERROR(s, "UuidToString() failed"); + std::string name; + try + { + name = reinterpret_cast<char*>(c); + } + catch (...) + { + RpcStringFreeA(&c); + throw; + } + RpcStringFreeA(&c); + named_pipe p("\\\\.\\pipe\\boost_process_" + name); + return p(stype); + } +}; +#endif + +/** + * Stream behavior to mute streams. + * + * A child process will be able to use streams. But data written to an + * output stream is discarded and data read from an input stream is 0. + */ +class null +{ +public: +#if defined(BOOST_POSIX_API) + null() + : stype_(unknown_stream) + { + } + + null(stream_type stype) + : stype_(stype) + { + } +#endif + + stream_ends operator()(stream_type stype) const + { +#if defined(BOOST_POSIX_API) + if (stype_ != unknown_stream) + stype = stype_; + std::string filename = (stype == input_stream) ? "/dev/zero" : + "/dev/null"; + int flag = (stype == input_stream) ? O_RDONLY : O_WRONLY; + handle child_end = open(filename.c_str(), flag); + if (!child_end.valid()) + BOOST_PROCESS_THROW_LAST_SYSTEM_ERROR("open(2) failed"); +#elif defined(BOOST_WINDOWS_API) + DWORD access = (stype == input_stream) ? GENERIC_READ : GENERIC_WRITE; + handle child_end = CreateFileA("NUL", access, 0, NULL, OPEN_EXISTING, + FILE_ATTRIBUTE_NORMAL, NULL); + if (!child_end.valid()) + BOOST_PROCESS_THROW_LAST_SYSTEM_ERROR("CreateFile() failed"); + if (!SetHandleInformation(child_end.native(), HANDLE_FLAG_INHERIT, + HANDLE_FLAG_INHERIT)) + BOOST_PROCESS_THROW_LAST_SYSTEM_ERROR( + "SetHandleInformation() failed"); +#endif + return stream_ends(child_end, handle()); + } + +#if defined(BOOST_POSIX_API) +private: + stream_type stype_; +#endif +}; + +} +} +} + +#endif
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/third_party/boost/process/stream_ends.hpp Thu Aug 25 11:05:55 2011 +0100 @@ -0,0 +1,68 @@ +// +// Boost.Process +// ~~~~~~~~~~~~~ +// +// Copyright (c) 2006, 2007 Julio M. Merino Vidal +// Copyright (c) 2008 Ilya Sokolov, Boris Schaeling +// Copyright (c) 2009 Boris Schaeling +// Copyright (c) 2010 Felipe Tanus, Boris Schaeling +// +// Distributed under the Boost Software License, Version 1.0. (See accompanying +// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) +// + +/** + * \file boost/process/stream_ends.hpp + * + * Includes the declaration of the stream_ends class. + */ + +#ifndef BOOST_PROCESS_STREAM_ENDS_HPP +#define BOOST_PROCESS_STREAM_ENDS_HPP + +#include <boost/process/config.hpp> +#include <boost/process/handle.hpp> + +namespace boost { +namespace process { + +/** + * A pair of handles to configure streams. + * + * Stream behaviors return a pair of handles to specify how a child's stream + * should be configured and possibly the opposite end of a child's end. This + * is the end remaining in the parent process and which can be used for example + * to communicate with a child process through its standard streams. + */ +struct stream_ends { + /** + * The child's end. + */ + handle child; + + /** + * The parent's end. + */ + handle parent; + + /** + * Standard constructor creating two invalid handles. + */ + stream_ends() + { + } + + /** + * Helper constructor to easily initialize handles. + */ + stream_ends(handle c, handle p) + : child(c), + parent(p) + { + } +}; + +} +} + +#endif
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/third_party/boost/process/stream_id.hpp Thu Aug 25 11:05:55 2011 +0100 @@ -0,0 +1,50 @@ +// +// Boost.Process +// ~~~~~~~~~~~~~ +// +// Copyright (c) 2006, 2007 Julio M. Merino Vidal +// Copyright (c) 2008 Ilya Sokolov, Boris Schaeling +// Copyright (c) 2009 Boris Schaeling +// Copyright (c) 2010 Felipe Tanus, Boris Schaeling +// +// Distributed under the Boost Software License, Version 1.0. (See accompanying +// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) +// + +/** + * \file boost/process/stream_id.hpp + * + * Includes the declaration of the stream_id type. + */ + +#ifndef BOOST_PROCESS_STREAM_ID_HPP +#define BOOST_PROCESS_STREAM_ID_HPP + +#include <boost/process/config.hpp> + +namespace boost { +namespace process { + +/** + * Standard stream id to refer to standard streams in a cross-platform manner. + */ +enum std_stream_id { stdin_id, stdout_id, stderr_id }; + +#if defined(BOOST_PROCESS_DOXYGEN) +/** + * Stream id type. + * + * Depending on the platform the stream id type is defined to refer to standard + * streams only or to support more streams. + */ +typedef NativeStreamId stream_id; +#elif defined(BOOST_POSIX_API) +typedef int stream_id; +#elif defined(BOOST_WINDOWS_API) +typedef std_stream_id stream_id; +#endif + +} +} + +#endif
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/third_party/boost/process/stream_type.hpp Thu Aug 25 11:05:55 2011 +0100 @@ -0,0 +1,45 @@ +// +// Boost.Process +// ~~~~~~~~~~~~~ +// +// Copyright (c) 2006, 2007 Julio M. Merino Vidal +// Copyright (c) 2008 Ilya Sokolov, Boris Schaeling +// Copyright (c) 2009 Boris Schaeling +// Copyright (c) 2010 Felipe Tanus, Boris Schaeling +// +// Distributed under the Boost Software License, Version 1.0. (See accompanying +// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) +// + +/** + * \file boost/process/stream_type.hpp + * + * Includes the declaration of the stream_type enumeration. + */ + +#ifndef BOOST_PROCESS_STREAM_TYPE_HPP +#define BOOST_PROCESS_STREAM_TYPE_HPP + +#include <boost/process/config.hpp> + +namespace boost { +namespace process { + +/** + * Stream type to differentiate between input and output streams. + * + * On POSIX systems another value unknown_stream is defined. It is passed + * to stream behaviors for file descriptors greater than 2. + */ +enum stream_type { + input_stream, + output_stream +#if defined(BOOST_POSIX_API) + , unknown_stream +#endif +}; + +} +} + +#endif
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/third_party/cmake_modules/FindFFTW.cmake Thu Aug 25 11:05:55 2011 +0100 @@ -0,0 +1,28 @@ +if (FFTW_INCLUDES AND FFTW_LIBRARIES) + set(FFTW_FIND_QUIETLY TRUE) +endif (FFTW_INCLUDES AND FFTW_LIBRARIES) + +find_path(FFTW_INCLUDES + NAMES + fftw3.h + PATHS + $ENV{FFTWDIR} + ${INCLUDE_INSTALL_DIR} +) + +find_library(FFTWF_LIB NAMES fftw3f PATHS $ENV{FFTWDIR} ${LIB_INSTALL_DIR}) +find_library(FFTW_LIB NAMES fftw3 PATHS $ENV{FFTWDIR} ${LIB_INSTALL_DIR}) +set(FFTW_LIBRARIES "${FFTWF_LIB};${FFTW_LIB}" ) + +find_library(FFTWL_LIB NAMES fftw3l PATHS $ENV{FFTWDIR} ${LIB_INSTALL_DIR}) + +if(FFTWL_LIB) +set(FFTW_LIBRARIES "${FFTW_LIBRARIES};${FFTWL_LIB}") +endif() + +include(FindPackageHandleStandardArgs) +find_package_handle_standard_args(FFTW DEFAULT_MSG + FFTW_INCLUDES FFTW_LIBRARIES) + +mark_as_advanced(FFTW_INCLUDES FFTW_LIBRARIES) +
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/third_party/cmake_modules/FindSndFile.cmake Thu Aug 25 11:05:55 2011 +0100 @@ -0,0 +1,55 @@ +# Locate sndfile +# This module defines +# SNDFILE_LIBRARY +# SNDFILE_FOUND, if false, do not try to link to sndfile +# SNDFILE_INCLUDE_DIR, where to find the headers +# +# $SNDFILE_DIR is an environment variable that would +# correspond to the ./configure --prefix=$SNDFILE_DIR + +FIND_PATH(SNDFILE_INCLUDE_DIR sndfile.h + PATH_SUFFIXES sndfile + PATHS + ${SNDFILE_DIR}/include + $ENV{SNDFILE_DIR}/include + $ENV{SNDFILE_DIR} + ~/Library/Frameworks + /Library/Frameworks + /usr/local/include + /usr/include + /usr/include/sndfile + /sw/include # Fink + /opt/local/include # DarwinPorts + /opt/csw/include # Blastwave + /opt/include + [HKEY_LOCAL_MACHINE\\SYSTEM\\CurrentControlSet\\Control\\Session\ Manager\\Environment;OSG_ROOT]/include + /usr/freeware/include +) + +find_library(SNDFILE_LIBRARY + NAMES "sndfile" + PATHS + ${SNDFILE_DIR}/lib + $ENV{SNDFILE_DIR}/lib + $ENV{SNDFILE_DIR} + ~/Library/Frameworks + /Library/Frameworks + /usr/local/lib + /usr/lib + /usr/lib64 + /sw/lib + /opt/local/lib + /opt/csw/lib + /opt/lib + [HKEY_LOCAL_MACHINE\\SYSTEM\\CurrentControlSet\\Control\\Session\ Manager\\Environment;OSG_ROOT]/lib + /usr/freeware/lib64 + PATH_SUFFIXES + win32 Darwin Linux "" +) + + +SET(SNDFILE_FOUND "NO") +IF(SNDFILE_LIBRARY AND SNDFILE_INCLUDE_DIR) + SET(SNDFILE_FOUND "YES") +ENDIF(SNDFILE_LIBRARY AND SNDFILE_INCLUDE_DIR) +
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/third_party/json/CMakeLists.txt Thu Aug 25 11:05:55 2011 +0100 @@ -0,0 +1,8 @@ +set(JSON_SOURCES + json_reader.cpp + json_value.cpp + json_writer.cpp +) + +add_library(json STATIC ${JSON_SOURCES}) +
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/third_party/json/LICENSE Thu Aug 25 11:05:55 2011 +0100 @@ -0,0 +1,55 @@ +The JsonCpp library's source code, including accompanying documentation, +tests and demonstration applications, are licensed under the following +conditions... + +The author (Baptiste Lepilleur) explicitly disclaims copyright in all +jurisdictions which recognize such a disclaimer. In such jurisdictions, +this software is released into the Public Domain. + +In jurisdictions which do not recognize Public Domain property (e.g. Germany as of +2010), this software is Copyright (c) 2007-2010 by Baptiste Lepilleur, and is +released under the terms of the MIT License (see below). + +In jurisdictions which recognize Public Domain property, the user of this +software may choose to accept it either as 1) Public Domain, 2) under the +conditions of the MIT License (see below), or 3) under the terms of dual +Public Domain/MIT License conditions described here, as they choose. + +The MIT License is about as close to Public Domain as a license can get, and is +described in clear, concise terms at: + + http://en.wikipedia.org/wiki/MIT_License + +The full text of the MIT License follows: + +======================================================================== +Copyright (c) 2007-2010 Baptiste Lepilleur + +Permission is hereby granted, free of charge, to any person +obtaining a copy of this software and associated documentation +files (the "Software"), to deal in the Software without +restriction, including without limitation the rights to use, copy, +modify, merge, publish, distribute, sublicense, and/or sell copies +of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be +included in all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, +EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF +MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND +NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS +BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN +ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN +CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. +======================================================================== +(END LICENSE TEXT) + +The MIT license is compatible with both the GPL and commercial +software, affording one all of the rights of Public Domain with the +minor nuisance of being required to keep the above copyright notice +and license text in the source code. Note also that by accepting the +Public Domain "license" you can re-license your copy using whatever +license you like.
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/third_party/json/assertions.h Thu Aug 25 11:05:55 2011 +0100 @@ -0,0 +1,26 @@ +// Copyright 2007-2010 Baptiste Lepilleur +// Distributed under MIT license, or public domain if desired and +// recognized in your jurisdiction. +// See file LICENSE for detail or copy at http://jsoncpp.sourceforge.net/LICENSE + +#ifndef CPPTL_JSON_ASSERTIONS_H_INCLUDED +# define CPPTL_JSON_ASSERTIONS_H_INCLUDED + +#include <stdlib.h> +#include <iostream> + +#if !defined(JSON_IS_AMALGAMATION) +# include <json/config.h> +#endif // if !defined(JSON_IS_AMALGAMATION) + +#if defined(JSON_USE_EXCEPTION) +#define JSON_ASSERT( condition ) assert( condition ); // @todo <= change this into an exception throw +#define JSON_FAIL_MESSAGE( message ) throw std::runtime_error( message ); +#else // defined(JSON_USE_EXCEPTION) +#define JSON_ASSERT( condition ) assert( condition ); +#define JSON_FAIL_MESSAGE( message ) { std::cerr << std::endl << message << std::endl; exit(123); } +#endif + +#define JSON_ASSERT_MESSAGE( condition, message ) if (!( condition )) { JSON_FAIL_MESSAGE( message ) } + +#endif // CPPTL_JSON_ASSERTIONS_H_INCLUDED
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/third_party/json/autolink.h Thu Aug 25 11:05:55 2011 +0100 @@ -0,0 +1,24 @@ +// Copyright 2007-2010 Baptiste Lepilleur +// Distributed under MIT license, or public domain if desired and +// recognized in your jurisdiction. +// See file LICENSE for detail or copy at http://jsoncpp.sourceforge.net/LICENSE + +#ifndef JSON_AUTOLINK_H_INCLUDED +# define JSON_AUTOLINK_H_INCLUDED + +# include "config.h" + +# ifdef JSON_IN_CPPTL +# include <cpptl/cpptl_autolink.h> +# endif + +# if !defined(JSON_NO_AUTOLINK) && !defined(JSON_DLL_BUILD) && !defined(JSON_IN_CPPTL) +# define CPPTL_AUTOLINK_NAME "json" +# undef CPPTL_AUTOLINK_DLL +# ifdef JSON_DLL +# define CPPTL_AUTOLINK_DLL +# endif +# include "autolink.h" +# endif + +#endif // JSON_AUTOLINK_H_INCLUDED
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/third_party/json/config.h Thu Aug 25 11:05:55 2011 +0100 @@ -0,0 +1,96 @@ +// Copyright 2007-2010 Baptiste Lepilleur +// Distributed under MIT license, or public domain if desired and +// recognized in your jurisdiction. +// See file LICENSE for detail or copy at http://jsoncpp.sourceforge.net/LICENSE + +#ifndef JSON_CONFIG_H_INCLUDED +# define JSON_CONFIG_H_INCLUDED + +/// If defined, indicates that json library is embedded in CppTL library. +//# define JSON_IN_CPPTL 1 + +/// If defined, indicates that json may leverage CppTL library +//# define JSON_USE_CPPTL 1 +/// If defined, indicates that cpptl vector based map should be used instead of std::map +/// as Value container. +//# define JSON_USE_CPPTL_SMALLMAP 1 +/// If defined, indicates that Json specific container should be used +/// (hash table & simple deque container with customizable allocator). +/// THIS FEATURE IS STILL EXPERIMENTAL! There is know bugs: See #3177332 +//# define JSON_VALUE_USE_INTERNAL_MAP 1 +/// Force usage of standard new/malloc based allocator instead of memory pool based allocator. +/// The memory pools allocator used optimization (initializing Value and ValueInternalLink +/// as if it was a POD) that may cause some validation tool to report errors. +/// Only has effects if JSON_VALUE_USE_INTERNAL_MAP is defined. +//# define JSON_USE_SIMPLE_INTERNAL_ALLOCATOR 1 + +/// If defined, indicates that Json use exception to report invalid type manipulation +/// instead of C assert macro. +# define JSON_USE_EXCEPTION 1 + +/// If defined, indicates that the source file is amalgated +/// to prevent private header inclusion. +/// Remarks: it is automatically defined in the generated amalgated header. +// #define JSON_IS_AMALGAMATION + + +# ifdef JSON_IN_CPPTL +# include <cpptl/config.h> +# ifndef JSON_USE_CPPTL +# define JSON_USE_CPPTL 1 +# endif +# endif + +# ifdef JSON_IN_CPPTL +# define JSON_API CPPTL_API +# elif defined(JSON_DLL_BUILD) +# define JSON_API __declspec(dllexport) +# elif defined(JSON_DLL) +# define JSON_API __declspec(dllimport) +# else +# define JSON_API +# endif + +// If JSON_NO_INT64 is defined, then Json only support C++ "int" type for integer +// Storages, and 64 bits integer support is disabled. +// #define JSON_NO_INT64 1 + +#if defined(_MSC_VER) && _MSC_VER <= 1200 // MSVC 6 +// Microsoft Visual Studio 6 only support conversion from __int64 to double +// (no conversion from unsigned __int64). +#define JSON_USE_INT64_DOUBLE_CONVERSION 1 +#endif // if defined(_MSC_VER) && _MSC_VER < 1200 // MSVC 6 + +#if defined(_MSC_VER) && _MSC_VER >= 1500 // MSVC 2008 +/// Indicates that the following function is deprecated. +# define JSONCPP_DEPRECATED(message) __declspec(deprecated(message)) +#endif + +#if !defined(JSONCPP_DEPRECATED) +# define JSONCPP_DEPRECATED(message) +#endif // if !defined(JSONCPP_DEPRECATED) + +namespace Json { + typedef int Int; + typedef unsigned int UInt; +# if defined(JSON_NO_INT64) + typedef int LargestInt; + typedef unsigned int LargestUInt; +# undef JSON_HAS_INT64 +# else // if defined(JSON_NO_INT64) + // For Microsoft Visual use specific types as long long is not supported +# if defined(_MSC_VER) // Microsoft Visual Studio + typedef __int64 Int64; + typedef unsigned __int64 UInt64; +# else // if defined(_MSC_VER) // Other platforms, use long long + typedef long long int Int64; + typedef unsigned long long int UInt64; +# endif // if defined(_MSC_VER) + typedef Int64 LargestInt; + typedef UInt64 LargestUInt; +# define JSON_HAS_INT64 +# endif // if defined(JSON_NO_INT64) +} // end namespace Json + + +#endif // JSON_CONFIG_H_INCLUDED
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/third_party/json/features.h Thu Aug 25 11:05:55 2011 +0100 @@ -0,0 +1,49 @@ +// Copyright 2007-2010 Baptiste Lepilleur +// Distributed under MIT license, or public domain if desired and +// recognized in your jurisdiction. +// See file LICENSE for detail or copy at http://jsoncpp.sourceforge.net/LICENSE + +#ifndef CPPTL_JSON_FEATURES_H_INCLUDED +# define CPPTL_JSON_FEATURES_H_INCLUDED + +#if !defined(JSON_IS_AMALGAMATION) +# include "forwards.h" +#endif // if !defined(JSON_IS_AMALGAMATION) + +namespace Json { + + /** \brief Configuration passed to reader and writer. + * This configuration object can be used to force the Reader or Writer + * to behave in a standard conforming way. + */ + class JSON_API Features + { + public: + /** \brief A configuration that allows all features and assumes all strings are UTF-8. + * - C & C++ comments are allowed + * - Root object can be any JSON value + * - Assumes Value strings are encoded in UTF-8 + */ + static Features all(); + + /** \brief A configuration that is strictly compatible with the JSON specification. + * - Comments are forbidden. + * - Root object must be either an array or an object value. + * - Assumes Value strings are encoded in UTF-8 + */ + static Features strictMode(); + + /** \brief Initialize the configuration like JsonConfig::allFeatures; + */ + Features(); + + /// \c true if comments are allowed. Default: \c true. + bool allowComments_; + + /// \c true if root must be either an array or an object value. Default: \c false. + bool strictRoot_; + }; + +} // namespace Json + +#endif // CPPTL_JSON_FEATURES_H_INCLUDED
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/third_party/json/forwards.h Thu Aug 25 11:05:55 2011 +0100 @@ -0,0 +1,44 @@ +// Copyright 2007-2010 Baptiste Lepilleur +// Distributed under MIT license, or public domain if desired and +// recognized in your jurisdiction. +// See file LICENSE for detail or copy at http://jsoncpp.sourceforge.net/LICENSE + +#ifndef JSON_FORWARDS_H_INCLUDED +# define JSON_FORWARDS_H_INCLUDED + +#if !defined(JSON_IS_AMALGAMATION) +# include "config.h" +#endif // if !defined(JSON_IS_AMALGAMATION) + +namespace Json { + + // writer.h + class FastWriter; + class StyledWriter; + + // reader.h + class Reader; + + // features.h + class Features; + + // value.h + typedef unsigned int ArrayIndex; + class StaticString; + class Path; + class PathArgument; + class Value; + class ValueIteratorBase; + class ValueIterator; + class ValueConstIterator; +#ifdef JSON_VALUE_USE_INTERNAL_MAP + class ValueMapAllocator; + class ValueInternalLink; + class ValueInternalArray; + class ValueInternalMap; +#endif // #ifdef JSON_VALUE_USE_INTERNAL_MAP + +} // namespace Json + + +#endif // JSON_FORWARDS_H_INCLUDED
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/third_party/json/json.h Thu Aug 25 11:05:55 2011 +0100 @@ -0,0 +1,15 @@ +// Copyright 2007-2010 Baptiste Lepilleur +// Distributed under MIT license, or public domain if desired and +// recognized in your jurisdiction. +// See file LICENSE for detail or copy at http://jsoncpp.sourceforge.net/LICENSE + +#ifndef JSON_JSON_H_INCLUDED +# define JSON_JSON_H_INCLUDED + +# include "autolink.h" +# include "value.h" +# include "reader.h" +# include "writer.h" +# include "features.h" + +#endif // JSON_JSON_H_INCLUDED
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/third_party/json/json_batchallocator.h Thu Aug 25 11:05:55 2011 +0100 @@ -0,0 +1,127 @@ +// Copyright 2007-2010 Baptiste Lepilleur +// Distributed under MIT license, or public domain if desired and +// recognized in your jurisdiction. +// See file LICENSE for detail or copy at http://jsoncpp.sourceforge.net/LICENSE + +#ifndef JSONCPP_BATCHALLOCATOR_H_INCLUDED +# define JSONCPP_BATCHALLOCATOR_H_INCLUDED + +# include <stdlib.h> +# include <assert.h> + +# ifndef JSONCPP_DOC_EXCLUDE_IMPLEMENTATION + +namespace Json { + +/* Fast memory allocator. + * + * This memory allocator allocates memory for a batch of object (specified by + * the page size, the number of object in each page). + * + * It does not allow the destruction of a single object. All the allocated objects + * can be destroyed at once. The memory can be either released or reused for future + * allocation. + * + * The in-place new operator must be used to construct the object using the pointer + * returned by allocate. + */ +template<typename AllocatedType + ,const unsigned int objectPerAllocation> +class BatchAllocator +{ +public: + BatchAllocator( unsigned int objectsPerPage = 255 ) + : freeHead_( 0 ) + , objectsPerPage_( objectsPerPage ) + { +// printf( "Size: %d => %s\n", sizeof(AllocatedType), typeid(AllocatedType).name() ); + assert( sizeof(AllocatedType) * objectPerAllocation >= sizeof(AllocatedType *) ); // We must be able to store a slist in the object free space. + assert( objectsPerPage >= 16 ); + batches_ = allocateBatch( 0 ); // allocated a dummy page + currentBatch_ = batches_; + } + + ~BatchAllocator() + { + for ( BatchInfo *batch = batches_; batch; ) + { + BatchInfo *nextBatch = batch->next_; + free( batch ); + batch = nextBatch; + } + } + + /// allocate space for an array of objectPerAllocation object. + /// @warning it is the responsability of the caller to call objects constructors. + AllocatedType *allocate() + { + if ( freeHead_ ) // returns node from free list. + { + AllocatedType *object = freeHead_; + freeHead_ = *(AllocatedType **)object; + return object; + } + if ( currentBatch_->used_ == currentBatch_->end_ ) + { + currentBatch_ = currentBatch_->next_; + while ( currentBatch_ && currentBatch_->used_ == currentBatch_->end_ ) + currentBatch_ = currentBatch_->next_; + + if ( !currentBatch_ ) // no free batch found, allocate a new one + { + currentBatch_ = allocateBatch( objectsPerPage_ ); + currentBatch_->next_ = batches_; // insert at the head of the list + batches_ = currentBatch_; + } + } + AllocatedType *allocated = currentBatch_->used_; + currentBatch_->used_ += objectPerAllocation; + return allocated; + } + + /// Release the object. + /// @warning it is the responsability of the caller to actually destruct the object. + void release( AllocatedType *object ) + { + assert( object != 0 ); + *(AllocatedType **)object = freeHead_; + freeHead_ = object; + } + +private: + struct BatchInfo + { + BatchInfo *next_; + AllocatedType *used_; + AllocatedType *end_; + AllocatedType buffer_[objectPerAllocation]; + }; + + // disabled copy constructor and assignement operator. + BatchAllocator( const BatchAllocator & ); + void operator =( const BatchAllocator &); + + static BatchInfo *allocateBatch( unsigned int objectsPerPage ) + { + const unsigned int mallocSize = sizeof(BatchInfo) - sizeof(AllocatedType)* objectPerAllocation + + sizeof(AllocatedType) * objectPerAllocation * objectsPerPage; + BatchInfo *batch = static_cast<BatchInfo*>( malloc( mallocSize ) ); + batch->next_ = 0; + batch->used_ = batch->buffer_; + batch->end_ = batch->buffer_ + objectsPerPage; + return batch; + } + + BatchInfo *batches_; + BatchInfo *currentBatch_; + /// Head of a single linked list within the allocated space of freeed object + AllocatedType *freeHead_; + unsigned int objectsPerPage_; +}; + + +} // namespace Json + +# endif // ifndef JSONCPP_DOC_INCLUDE_IMPLEMENTATION + +#endif // JSONCPP_BATCHALLOCATOR_H_INCLUDED
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/third_party/json/json_internalarray.inl Thu Aug 25 11:05:55 2011 +0100 @@ -0,0 +1,454 @@ +// Copyright 2007-2010 Baptiste Lepilleur +// Distributed under MIT license, or public domain if desired and +// recognized in your jurisdiction. +// See file LICENSE for detail or copy at http://jsoncpp.sourceforge.net/LICENSE + +// included by json_value.cpp + +namespace Json { + +// ////////////////////////////////////////////////////////////////// +// ////////////////////////////////////////////////////////////////// +// ////////////////////////////////////////////////////////////////// +// class ValueInternalArray +// ////////////////////////////////////////////////////////////////// +// ////////////////////////////////////////////////////////////////// +// ////////////////////////////////////////////////////////////////// + +ValueArrayAllocator::~ValueArrayAllocator() +{ +} + +// ////////////////////////////////////////////////////////////////// +// class DefaultValueArrayAllocator +// ////////////////////////////////////////////////////////////////// +#ifdef JSON_USE_SIMPLE_INTERNAL_ALLOCATOR +class DefaultValueArrayAllocator : public ValueArrayAllocator +{ +public: // overridden from ValueArrayAllocator + virtual ~DefaultValueArrayAllocator() + { + } + + virtual ValueInternalArray *newArray() + { + return new ValueInternalArray(); + } + + virtual ValueInternalArray *newArrayCopy( const ValueInternalArray &other ) + { + return new ValueInternalArray( other ); + } + + virtual void destructArray( ValueInternalArray *array ) + { + delete array; + } + + virtual void reallocateArrayPageIndex( Value **&indexes, + ValueInternalArray::PageIndex &indexCount, + ValueInternalArray::PageIndex minNewIndexCount ) + { + ValueInternalArray::PageIndex newIndexCount = (indexCount*3)/2 + 1; + if ( minNewIndexCount > newIndexCount ) + newIndexCount = minNewIndexCount; + void *newIndexes = realloc( indexes, sizeof(Value*) * newIndexCount ); + JSON_ASSERT_MESSAGE(newIndexes, "Couldn't realloc."); + indexCount = newIndexCount; + indexes = static_cast<Value **>( newIndexes ); + } + virtual void releaseArrayPageIndex( Value **indexes, + ValueInternalArray::PageIndex indexCount ) + { + if ( indexes ) + free( indexes ); + } + + virtual Value *allocateArrayPage() + { + return static_cast<Value *>( malloc( sizeof(Value) * ValueInternalArray::itemsPerPage ) ); + } + + virtual void releaseArrayPage( Value *value ) + { + if ( value ) + free( value ); + } +}; + +#else // #ifdef JSON_USE_SIMPLE_INTERNAL_ALLOCATOR +/// @todo make this thread-safe (lock when accessign batch allocator) +class DefaultValueArrayAllocator : public ValueArrayAllocator +{ +public: // overridden from ValueArrayAllocator + virtual ~DefaultValueArrayAllocator() + { + } + + virtual ValueInternalArray *newArray() + { + ValueInternalArray *array = arraysAllocator_.allocate(); + new (array) ValueInternalArray(); // placement new + return array; + } + + virtual ValueInternalArray *newArrayCopy( const ValueInternalArray &other ) + { + ValueInternalArray *array = arraysAllocator_.allocate(); + new (array) ValueInternalArray( other ); // placement new + return array; + } + + virtual void destructArray( ValueInternalArray *array ) + { + if ( array ) + { + array->~ValueInternalArray(); + arraysAllocator_.release( array ); + } + } + + virtual void reallocateArrayPageIndex( Value **&indexes, + ValueInternalArray::PageIndex &indexCount, + ValueInternalArray::PageIndex minNewIndexCount ) + { + ValueInternalArray::PageIndex newIndexCount = (indexCount*3)/2 + 1; + if ( minNewIndexCount > newIndexCount ) + newIndexCount = minNewIndexCount; + void *newIndexes = realloc( indexes, sizeof(Value*) * newIndexCount ); + JSON_ASSERT_MESSAGE(newIndexes, "Couldn't realloc."); + indexCount = newIndexCount; + indexes = static_cast<Value **>( newIndexes ); + } + virtual void releaseArrayPageIndex( Value **indexes, + ValueInternalArray::PageIndex indexCount ) + { + if ( indexes ) + free( indexes ); + } + + virtual Value *allocateArrayPage() + { + return static_cast<Value *>( pagesAllocator_.allocate() ); + } + + virtual void releaseArrayPage( Value *value ) + { + if ( value ) + pagesAllocator_.release( value ); + } +private: + BatchAllocator<ValueInternalArray,1> arraysAllocator_; + BatchAllocator<Value,ValueInternalArray::itemsPerPage> pagesAllocator_; +}; +#endif // #ifdef JSON_USE_SIMPLE_INTERNAL_ALLOCATOR + +static ValueArrayAllocator *&arrayAllocator() +{ + static DefaultValueArrayAllocator defaultAllocator; + static ValueArrayAllocator *arrayAllocator = &defaultAllocator; + return arrayAllocator; +} + +static struct DummyArrayAllocatorInitializer { + DummyArrayAllocatorInitializer() + { + arrayAllocator(); // ensure arrayAllocator() statics are initialized before main(). + } +} dummyArrayAllocatorInitializer; + +// ////////////////////////////////////////////////////////////////// +// class ValueInternalArray +// ////////////////////////////////////////////////////////////////// +bool +ValueInternalArray::equals( const IteratorState &x, + const IteratorState &other ) +{ + return x.array_ == other.array_ + && x.currentItemIndex_ == other.currentItemIndex_ + && x.currentPageIndex_ == other.currentPageIndex_; +} + + +void +ValueInternalArray::increment( IteratorState &it ) +{ + JSON_ASSERT_MESSAGE( it.array_ && + (it.currentPageIndex_ - it.array_->pages_)*itemsPerPage + it.currentItemIndex_ + != it.array_->size_, + "ValueInternalArray::increment(): moving iterator beyond end" ); + ++(it.currentItemIndex_); + if ( it.currentItemIndex_ == itemsPerPage ) + { + it.currentItemIndex_ = 0; + ++(it.currentPageIndex_); + } +} + + +void +ValueInternalArray::decrement( IteratorState &it ) +{ + JSON_ASSERT_MESSAGE( it.array_ && it.currentPageIndex_ == it.array_->pages_ + && it.currentItemIndex_ == 0, + "ValueInternalArray::decrement(): moving iterator beyond end" ); + if ( it.currentItemIndex_ == 0 ) + { + it.currentItemIndex_ = itemsPerPage-1; + --(it.currentPageIndex_); + } + else + { + --(it.currentItemIndex_); + } +} + + +Value & +ValueInternalArray::unsafeDereference( const IteratorState &it ) +{ + return (*(it.currentPageIndex_))[it.currentItemIndex_]; +} + + +Value & +ValueInternalArray::dereference( const IteratorState &it ) +{ + JSON_ASSERT_MESSAGE( it.array_ && + (it.currentPageIndex_ - it.array_->pages_)*itemsPerPage + it.currentItemIndex_ + < it.array_->size_, + "ValueInternalArray::dereference(): dereferencing invalid iterator" ); + return unsafeDereference( it ); +} + +void +ValueInternalArray::makeBeginIterator( IteratorState &it ) const +{ + it.array_ = const_cast<ValueInternalArray *>( this ); + it.currentItemIndex_ = 0; + it.currentPageIndex_ = pages_; +} + + +void +ValueInternalArray::makeIterator( IteratorState &it, ArrayIndex index ) const +{ + it.array_ = const_cast<ValueInternalArray *>( this ); + it.currentItemIndex_ = index % itemsPerPage; + it.currentPageIndex_ = pages_ + index / itemsPerPage; +} + + +void +ValueInternalArray::makeEndIterator( IteratorState &it ) const +{ + makeIterator( it, size_ ); +} + + +ValueInternalArray::ValueInternalArray() + : pages_( 0 ) + , size_( 0 ) + , pageCount_( 0 ) +{ +} + + +ValueInternalArray::ValueInternalArray( const ValueInternalArray &other ) + : pages_( 0 ) + , size_( other.size_ ) + , pageCount_( 0 ) +{ + PageIndex minNewPages = other.size_ / itemsPerPage; + arrayAllocator()->reallocateArrayPageIndex( pages_, pageCount_, minNewPages ); + JSON_ASSERT_MESSAGE( pageCount_ >= minNewPages, + "ValueInternalArray::reserve(): bad reallocation" ); + IteratorState itOther; + other.makeBeginIterator( itOther ); + Value *value; + for ( ArrayIndex index = 0; index < size_; ++index, increment(itOther) ) + { + if ( index % itemsPerPage == 0 ) + { + PageIndex pageIndex = index / itemsPerPage; + value = arrayAllocator()->allocateArrayPage(); + pages_[pageIndex] = value; + } + new (value) Value( dereference( itOther ) ); + } +} + + +ValueInternalArray & +ValueInternalArray::operator =( const ValueInternalArray &other ) +{ + ValueInternalArray temp( other ); + swap( temp ); + return *this; +} + + +ValueInternalArray::~ValueInternalArray() +{ + // destroy all constructed items + IteratorState it; + IteratorState itEnd; + makeBeginIterator( it); + makeEndIterator( itEnd ); + for ( ; !equals(it,itEnd); increment(it) ) + { + Value *value = &dereference(it); + value->~Value(); + } + // release all pages + PageIndex lastPageIndex = size_ / itemsPerPage; + for ( PageIndex pageIndex = 0; pageIndex < lastPageIndex; ++pageIndex ) + arrayAllocator()->releaseArrayPage( pages_[pageIndex] ); + // release pages index + arrayAllocator()->releaseArrayPageIndex( pages_, pageCount_ ); +} + + +void +ValueInternalArray::swap( ValueInternalArray &other ) +{ + Value **tempPages = pages_; + pages_ = other.pages_; + other.pages_ = tempPages; + ArrayIndex tempSize = size_; + size_ = other.size_; + other.size_ = tempSize; + PageIndex tempPageCount = pageCount_; + pageCount_ = other.pageCount_; + other.pageCount_ = tempPageCount; +} + +void +ValueInternalArray::clear() +{ + ValueInternalArray dummy; + swap( dummy ); +} + + +void +ValueInternalArray::resize( ArrayIndex newSize ) +{ + if ( newSize == 0 ) + clear(); + else if ( newSize < size_ ) + { + IteratorState it; + IteratorState itEnd; + makeIterator( it, newSize ); + makeIterator( itEnd, size_ ); + for ( ; !equals(it,itEnd); increment(it) ) + { + Value *value = &dereference(it); + value->~Value(); + } + PageIndex pageIndex = (newSize + itemsPerPage - 1) / itemsPerPage; + PageIndex lastPageIndex = size_ / itemsPerPage; + for ( ; pageIndex < lastPageIndex; ++pageIndex ) + arrayAllocator()->releaseArrayPage( pages_[pageIndex] ); + size_ = newSize; + } + else if ( newSize > size_ ) + resolveReference( newSize ); +} + + +void +ValueInternalArray::makeIndexValid( ArrayIndex index ) +{ + // Need to enlarge page index ? + if ( index >= pageCount_ * itemsPerPage ) + { + PageIndex minNewPages = (index + 1) / itemsPerPage; + arrayAllocator()->reallocateArrayPageIndex( pages_, pageCount_, minNewPages ); + JSON_ASSERT_MESSAGE( pageCount_ >= minNewPages, "ValueInternalArray::reserve(): bad reallocation" ); + } + + // Need to allocate new pages ? + ArrayIndex nextPageIndex = + (size_ % itemsPerPage) != 0 ? size_ - (size_%itemsPerPage) + itemsPerPage + : size_; + if ( nextPageIndex <= index ) + { + PageIndex pageIndex = nextPageIndex / itemsPerPage; + PageIndex pageToAllocate = (index - nextPageIndex) / itemsPerPage + 1; + for ( ; pageToAllocate-- > 0; ++pageIndex ) + pages_[pageIndex] = arrayAllocator()->allocateArrayPage(); + } + + // Initialize all new entries + IteratorState it; + IteratorState itEnd; + makeIterator( it, size_ ); + size_ = index + 1; + makeIterator( itEnd, size_ ); + for ( ; !equals(it,itEnd); increment(it) ) + { + Value *value = &dereference(it); + new (value) Value(); // Construct a default value using placement new + } +} + +Value & +ValueInternalArray::resolveReference( ArrayIndex index ) +{ + if ( index >= size_ ) + makeIndexValid( index ); + return pages_[index/itemsPerPage][index%itemsPerPage]; +} + +Value * +ValueInternalArray::find( ArrayIndex index ) const +{ + if ( index >= size_ ) + return 0; + return &(pages_[index/itemsPerPage][index%itemsPerPage]); +} + +ValueInternalArray::ArrayIndex +ValueInternalArray::size() const +{ + return size_; +} + +int +ValueInternalArray::distance( const IteratorState &x, const IteratorState &y ) +{ + return indexOf(y) - indexOf(x); +} + + +ValueInternalArray::ArrayIndex +ValueInternalArray::indexOf( const IteratorState &iterator ) +{ + if ( !iterator.array_ ) + return ArrayIndex(-1); + return ArrayIndex( + (iterator.currentPageIndex_ - iterator.array_->pages_) * itemsPerPage + + iterator.currentItemIndex_ ); +} + + +int +ValueInternalArray::compare( const ValueInternalArray &other ) const +{ + int sizeDiff( size_ - other.size_ ); + if ( sizeDiff != 0 ) + return sizeDiff; + + for ( ArrayIndex index =0; index < size_; ++index ) + { + int diff = pages_[index/itemsPerPage][index%itemsPerPage].compare( + other.pages_[index/itemsPerPage][index%itemsPerPage] ); + if ( diff != 0 ) + return diff; + } + return 0; +} + +} // namespace Json
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/third_party/json/json_internalmap.inl Thu Aug 25 11:05:55 2011 +0100 @@ -0,0 +1,615 @@ +// Copyright 2007-2010 Baptiste Lepilleur +// Distributed under MIT license, or public domain if desired and +// recognized in your jurisdiction. +// See file LICENSE for detail or copy at http://jsoncpp.sourceforge.net/LICENSE + +// included by json_value.cpp + +namespace Json { + +// ////////////////////////////////////////////////////////////////// +// ////////////////////////////////////////////////////////////////// +// ////////////////////////////////////////////////////////////////// +// class ValueInternalMap +// ////////////////////////////////////////////////////////////////// +// ////////////////////////////////////////////////////////////////// +// ////////////////////////////////////////////////////////////////// + +/** \internal MUST be safely initialized using memset( this, 0, sizeof(ValueInternalLink) ); + * This optimization is used by the fast allocator. + */ +ValueInternalLink::ValueInternalLink() + : previous_( 0 ) + , next_( 0 ) +{ +} + +ValueInternalLink::~ValueInternalLink() +{ + for ( int index =0; index < itemPerLink; ++index ) + { + if ( !items_[index].isItemAvailable() ) + { + if ( !items_[index].isMemberNameStatic() ) + free( keys_[index] ); + } + else + break; + } +} + + + +ValueMapAllocator::~ValueMapAllocator() +{ +} + +#ifdef JSON_USE_SIMPLE_INTERNAL_ALLOCATOR +class DefaultValueMapAllocator : public ValueMapAllocator +{ +public: // overridden from ValueMapAllocator + virtual ValueInternalMap *newMap() + { + return new ValueInternalMap(); + } + + virtual ValueInternalMap *newMapCopy( const ValueInternalMap &other ) + { + return new ValueInternalMap( other ); + } + + virtual void destructMap( ValueInternalMap *map ) + { + delete map; + } + + virtual ValueInternalLink *allocateMapBuckets( unsigned int size ) + { + return new ValueInternalLink[size]; + } + + virtual void releaseMapBuckets( ValueInternalLink *links ) + { + delete [] links; + } + + virtual ValueInternalLink *allocateMapLink() + { + return new ValueInternalLink(); + } + + virtual void releaseMapLink( ValueInternalLink *link ) + { + delete link; + } +}; +#else +/// @todo make this thread-safe (lock when accessign batch allocator) +class DefaultValueMapAllocator : public ValueMapAllocator +{ +public: // overridden from ValueMapAllocator + virtual ValueInternalMap *newMap() + { + ValueInternalMap *map = mapsAllocator_.allocate(); + new (map) ValueInternalMap(); // placement new + return map; + } + + virtual ValueInternalMap *newMapCopy( const ValueInternalMap &other ) + { + ValueInternalMap *map = mapsAllocator_.allocate(); + new (map) ValueInternalMap( other ); // placement new + return map; + } + + virtual void destructMap( ValueInternalMap *map ) + { + if ( map ) + { + map->~ValueInternalMap(); + mapsAllocator_.release( map ); + } + } + + virtual ValueInternalLink *allocateMapBuckets( unsigned int size ) + { + return new ValueInternalLink[size]; + } + + virtual void releaseMapBuckets( ValueInternalLink *links ) + { + delete [] links; + } + + virtual ValueInternalLink *allocateMapLink() + { + ValueInternalLink *link = linksAllocator_.allocate(); + memset( link, 0, sizeof(ValueInternalLink) ); + return link; + } + + virtual void releaseMapLink( ValueInternalLink *link ) + { + link->~ValueInternalLink(); + linksAllocator_.release( link ); + } +private: + BatchAllocator<ValueInternalMap,1> mapsAllocator_; + BatchAllocator<ValueInternalLink,1> linksAllocator_; +}; +#endif + +static ValueMapAllocator *&mapAllocator() +{ + static DefaultValueMapAllocator defaultAllocator; + static ValueMapAllocator *mapAllocator = &defaultAllocator; + return mapAllocator; +} + +static struct DummyMapAllocatorInitializer { + DummyMapAllocatorInitializer() + { + mapAllocator(); // ensure mapAllocator() statics are initialized before main(). + } +} dummyMapAllocatorInitializer; + + + +// h(K) = value * K >> w ; with w = 32 & K prime w.r.t. 2^32. + +/* +use linked list hash map. +buckets array is a container. +linked list element contains 6 key/values. (memory = (16+4) * 6 + 4 = 124) +value have extra state: valid, available, deleted +*/ + + +ValueInternalMap::ValueInternalMap() + : buckets_( 0 ) + , tailLink_( 0 ) + , bucketsSize_( 0 ) + , itemCount_( 0 ) +{ +} + + +ValueInternalMap::ValueInternalMap( const ValueInternalMap &other ) + : buckets_( 0 ) + , tailLink_( 0 ) + , bucketsSize_( 0 ) + , itemCount_( 0 ) +{ + reserve( other.itemCount_ ); + IteratorState it; + IteratorState itEnd; + other.makeBeginIterator( it ); + other.makeEndIterator( itEnd ); + for ( ; !equals(it,itEnd); increment(it) ) + { + bool isStatic; + const char *memberName = key( it, isStatic ); + const Value &aValue = value( it ); + resolveReference(memberName, isStatic) = aValue; + } +} + + +ValueInternalMap & +ValueInternalMap::operator =( const ValueInternalMap &other ) +{ + ValueInternalMap dummy( other ); + swap( dummy ); + return *this; +} + + +ValueInternalMap::~ValueInternalMap() +{ + if ( buckets_ ) + { + for ( BucketIndex bucketIndex =0; bucketIndex < bucketsSize_; ++bucketIndex ) + { + ValueInternalLink *link = buckets_[bucketIndex].next_; + while ( link ) + { + ValueInternalLink *linkToRelease = link; + link = link->next_; + mapAllocator()->releaseMapLink( linkToRelease ); + } + } + mapAllocator()->releaseMapBuckets( buckets_ ); + } +} + + +void +ValueInternalMap::swap( ValueInternalMap &other ) +{ + ValueInternalLink *tempBuckets = buckets_; + buckets_ = other.buckets_; + other.buckets_ = tempBuckets; + ValueInternalLink *tempTailLink = tailLink_; + tailLink_ = other.tailLink_; + other.tailLink_ = tempTailLink; + BucketIndex tempBucketsSize = bucketsSize_; + bucketsSize_ = other.bucketsSize_; + other.bucketsSize_ = tempBucketsSize; + BucketIndex tempItemCount = itemCount_; + itemCount_ = other.itemCount_; + other.itemCount_ = tempItemCount; +} + + +void +ValueInternalMap::clear() +{ + ValueInternalMap dummy; + swap( dummy ); +} + + +ValueInternalMap::BucketIndex +ValueInternalMap::size() const +{ + return itemCount_; +} + +bool +ValueInternalMap::reserveDelta( BucketIndex growth ) +{ + return reserve( itemCount_ + growth ); +} + +bool +ValueInternalMap::reserve( BucketIndex newItemCount ) +{ + if ( !buckets_ && newItemCount > 0 ) + { + buckets_ = mapAllocator()->allocateMapBuckets( 1 ); + bucketsSize_ = 1; + tailLink_ = &buckets_[0]; + } +// BucketIndex idealBucketCount = (newItemCount + ValueInternalLink::itemPerLink) / ValueInternalLink::itemPerLink; + return true; +} + + +const Value * +ValueInternalMap::find( const char *key ) const +{ + if ( !bucketsSize_ ) + return 0; + HashKey hashedKey = hash( key ); + BucketIndex bucketIndex = hashedKey % bucketsSize_; + for ( const ValueInternalLink *current = &buckets_[bucketIndex]; + current != 0; + current = current->next_ ) + { + for ( BucketIndex index=0; index < ValueInternalLink::itemPerLink; ++index ) + { + if ( current->items_[index].isItemAvailable() ) + return 0; + if ( strcmp( key, current->keys_[index] ) == 0 ) + return ¤t->items_[index]; + } + } + return 0; +} + + +Value * +ValueInternalMap::find( const char *key ) +{ + const ValueInternalMap *constThis = this; + return const_cast<Value *>( constThis->find( key ) ); +} + + +Value & +ValueInternalMap::resolveReference( const char *key, + bool isStatic ) +{ + HashKey hashedKey = hash( key ); + if ( bucketsSize_ ) + { + BucketIndex bucketIndex = hashedKey % bucketsSize_; + ValueInternalLink **previous = 0; + BucketIndex index; + for ( ValueInternalLink *current = &buckets_[bucketIndex]; + current != 0; + previous = ¤t->next_, current = current->next_ ) + { + for ( index=0; index < ValueInternalLink::itemPerLink; ++index ) + { + if ( current->items_[index].isItemAvailable() ) + return setNewItem( key, isStatic, current, index ); + if ( strcmp( key, current->keys_[index] ) == 0 ) + return current->items_[index]; + } + } + } + + reserveDelta( 1 ); + return unsafeAdd( key, isStatic, hashedKey ); +} + + +void +ValueInternalMap::remove( const char *key ) +{ + HashKey hashedKey = hash( key ); + if ( !bucketsSize_ ) + return; + BucketIndex bucketIndex = hashedKey % bucketsSize_; + for ( ValueInternalLink *link = &buckets_[bucketIndex]; + link != 0; + link = link->next_ ) + { + BucketIndex index; + for ( index =0; index < ValueInternalLink::itemPerLink; ++index ) + { + if ( link->items_[index].isItemAvailable() ) + return; + if ( strcmp( key, link->keys_[index] ) == 0 ) + { + doActualRemove( link, index, bucketIndex ); + return; + } + } + } +} + +void +ValueInternalMap::doActualRemove( ValueInternalLink *link, + BucketIndex index, + BucketIndex bucketIndex ) +{ + // find last item of the bucket and swap it with the 'removed' one. + // set removed items flags to 'available'. + // if last page only contains 'available' items, then desallocate it (it's empty) + ValueInternalLink *&lastLink = getLastLinkInBucket( index ); + BucketIndex lastItemIndex = 1; // a link can never be empty, so start at 1 + for ( ; + lastItemIndex < ValueInternalLink::itemPerLink; + ++lastItemIndex ) // may be optimized with dicotomic search + { + if ( lastLink->items_[lastItemIndex].isItemAvailable() ) + break; + } + + BucketIndex lastUsedIndex = lastItemIndex - 1; + Value *valueToDelete = &link->items_[index]; + Value *valueToPreserve = &lastLink->items_[lastUsedIndex]; + if ( valueToDelete != valueToPreserve ) + valueToDelete->swap( *valueToPreserve ); + if ( lastUsedIndex == 0 ) // page is now empty + { // remove it from bucket linked list and delete it. + ValueInternalLink *linkPreviousToLast = lastLink->previous_; + if ( linkPreviousToLast != 0 ) // can not deleted bucket link. + { + mapAllocator()->releaseMapLink( lastLink ); + linkPreviousToLast->next_ = 0; + lastLink = linkPreviousToLast; + } + } + else + { + Value dummy; + valueToPreserve->swap( dummy ); // restore deleted to default Value. + valueToPreserve->setItemUsed( false ); + } + --itemCount_; +} + + +ValueInternalLink *& +ValueInternalMap::getLastLinkInBucket( BucketIndex bucketIndex ) +{ + if ( bucketIndex == bucketsSize_ - 1 ) + return tailLink_; + ValueInternalLink *&previous = buckets_[bucketIndex+1].previous_; + if ( !previous ) + previous = &buckets_[bucketIndex]; + return previous; +} + + +Value & +ValueInternalMap::setNewItem( const char *key, + bool isStatic, + ValueInternalLink *link, + BucketIndex index ) +{ + char *duplicatedKey = makeMemberName( key ); + ++itemCount_; + link->keys_[index] = duplicatedKey; + link->items_[index].setItemUsed(); + link->items_[index].setMemberNameIsStatic( isStatic ); + return link->items_[index]; // items already default constructed. +} + + +Value & +ValueInternalMap::unsafeAdd( const char *key, + bool isStatic, + HashKey hashedKey ) +{ + JSON_ASSERT_MESSAGE( bucketsSize_ > 0, "ValueInternalMap::unsafeAdd(): internal logic error." ); + BucketIndex bucketIndex = hashedKey % bucketsSize_; + ValueInternalLink *&previousLink = getLastLinkInBucket( bucketIndex ); + ValueInternalLink *link = previousLink; + BucketIndex index; + for ( index =0; index < ValueInternalLink::itemPerLink; ++index ) + { + if ( link->items_[index].isItemAvailable() ) + break; + } + if ( index == ValueInternalLink::itemPerLink ) // need to add a new page + { + ValueInternalLink *newLink = mapAllocator()->allocateMapLink(); + index = 0; + link->next_ = newLink; + previousLink = newLink; + link = newLink; + } + return setNewItem( key, isStatic, link, index ); +} + + +ValueInternalMap::HashKey +ValueInternalMap::hash( const char *key ) const +{ + HashKey hash = 0; + while ( *key ) + hash += *key++ * 37; + return hash; +} + + +int +ValueInternalMap::compare( const ValueInternalMap &other ) const +{ + int sizeDiff( itemCount_ - other.itemCount_ ); + if ( sizeDiff != 0 ) + return sizeDiff; + // Strict order guaranty is required. Compare all keys FIRST, then compare values. + IteratorState it; + IteratorState itEnd; + makeBeginIterator( it ); + makeEndIterator( itEnd ); + for ( ; !equals(it,itEnd); increment(it) ) + { + if ( !other.find( key( it ) ) ) + return 1; + } + + // All keys are equals, let's compare values + makeBeginIterator( it ); + for ( ; !equals(it,itEnd); increment(it) ) + { + const Value *otherValue = other.find( key( it ) ); + int valueDiff = value(it).compare( *otherValue ); + if ( valueDiff != 0 ) + return valueDiff; + } + return 0; +} + + +void +ValueInternalMap::makeBeginIterator( IteratorState &it ) const +{ + it.map_ = const_cast<ValueInternalMap *>( this ); + it.bucketIndex_ = 0; + it.itemIndex_ = 0; + it.link_ = buckets_; +} + + +void +ValueInternalMap::makeEndIterator( IteratorState &it ) const +{ + it.map_ = const_cast<ValueInternalMap *>( this ); + it.bucketIndex_ = bucketsSize_; + it.itemIndex_ = 0; + it.link_ = 0; +} + + +bool +ValueInternalMap::equals( const IteratorState &x, const IteratorState &other ) +{ + return x.map_ == other.map_ + && x.bucketIndex_ == other.bucketIndex_ + && x.link_ == other.link_ + && x.itemIndex_ == other.itemIndex_; +} + + +void +ValueInternalMap::incrementBucket( IteratorState &iterator ) +{ + ++iterator.bucketIndex_; + JSON_ASSERT_MESSAGE( iterator.bucketIndex_ <= iterator.map_->bucketsSize_, + "ValueInternalMap::increment(): attempting to iterate beyond end." ); + if ( iterator.bucketIndex_ == iterator.map_->bucketsSize_ ) + iterator.link_ = 0; + else + iterator.link_ = &(iterator.map_->buckets_[iterator.bucketIndex_]); + iterator.itemIndex_ = 0; +} + + +void +ValueInternalMap::increment( IteratorState &iterator ) +{ + JSON_ASSERT_MESSAGE( iterator.map_, "Attempting to iterator using invalid iterator." ); + ++iterator.itemIndex_; + if ( iterator.itemIndex_ == ValueInternalLink::itemPerLink ) + { + JSON_ASSERT_MESSAGE( iterator.link_ != 0, + "ValueInternalMap::increment(): attempting to iterate beyond end." ); + iterator.link_ = iterator.link_->next_; + if ( iterator.link_ == 0 ) + incrementBucket( iterator ); + } + else if ( iterator.link_->items_[iterator.itemIndex_].isItemAvailable() ) + { + incrementBucket( iterator ); + } +} + + +void +ValueInternalMap::decrement( IteratorState &iterator ) +{ + if ( iterator.itemIndex_ == 0 ) + { + JSON_ASSERT_MESSAGE( iterator.map_, "Attempting to iterate using invalid iterator." ); + if ( iterator.link_ == &iterator.map_->buckets_[iterator.bucketIndex_] ) + { + JSON_ASSERT_MESSAGE( iterator.bucketIndex_ > 0, "Attempting to iterate beyond beginning." ); + --(iterator.bucketIndex_); + } + iterator.link_ = iterator.link_->previous_; + iterator.itemIndex_ = ValueInternalLink::itemPerLink - 1; + } +} + + +const char * +ValueInternalMap::key( const IteratorState &iterator ) +{ + JSON_ASSERT_MESSAGE( iterator.link_, "Attempting to iterate using invalid iterator." ); + return iterator.link_->keys_[iterator.itemIndex_]; +} + +const char * +ValueInternalMap::key( const IteratorState &iterator, bool &isStatic ) +{ + JSON_ASSERT_MESSAGE( iterator.link_, "Attempting to iterate using invalid iterator." ); + isStatic = iterator.link_->items_[iterator.itemIndex_].isMemberNameStatic(); + return iterator.link_->keys_[iterator.itemIndex_]; +} + + +Value & +ValueInternalMap::value( const IteratorState &iterator ) +{ + JSON_ASSERT_MESSAGE( iterator.link_, "Attempting to iterate using invalid iterator." ); + return iterator.link_->items_[iterator.itemIndex_]; +} + + +int +ValueInternalMap::distance( const IteratorState &x, const IteratorState &y ) +{ + int offset = 0; + IteratorState it = x; + while ( !equals( it, y ) ) + increment( it ); + return offset; +} + +} // namespace Json
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/third_party/json/json_reader.cpp Thu Aug 25 11:05:55 2011 +0100 @@ -0,0 +1,912 @@ +// Copyright 2007-2011 Baptiste Lepilleur +// Distributed under MIT license, or public domain if desired and +// recognized in your jurisdiction. +// See file LICENSE for detail or copy at http://jsoncpp.sourceforge.net/LICENSE + +#if !defined(JSON_IS_AMALGAMATION) +# include <json/assertions.h> +# include <json/reader.h> +# include <json/value.h> +# include "json_tool.h" +#endif // if !defined(JSON_IS_AMALGAMATION) +#include <utility> +#include <cstdio> +#include <cassert> +#include <cstring> +#include <iostream> +#include <stdexcept> + +#if _MSC_VER >= 1400 // VC++ 8.0 +#pragma warning( disable : 4996 ) // disable warning about strdup being deprecated. +#endif + +namespace Json { + +// Implementation of class Features +// //////////////////////////////// + +Features::Features() + : allowComments_( true ) + , strictRoot_( false ) +{ +} + + +Features +Features::all() +{ + return Features(); +} + + +Features +Features::strictMode() +{ + Features features; + features.allowComments_ = false; + features.strictRoot_ = true; + return features; +} + +// Implementation of class Reader +// //////////////////////////////// + + +static inline bool +in( Reader::Char c, Reader::Char c1, Reader::Char c2, Reader::Char c3, Reader::Char c4 ) +{ + return c == c1 || c == c2 || c == c3 || c == c4; +} + +static inline bool +in( Reader::Char c, Reader::Char c1, Reader::Char c2, Reader::Char c3, Reader::Char c4, Reader::Char c5 ) +{ + return c == c1 || c == c2 || c == c3 || c == c4 || c == c5; +} + + +static bool +containsNewLine( Reader::Location begin, + Reader::Location end ) +{ + for ( ;begin < end; ++begin ) + if ( *begin == '\n' || *begin == '\r' ) + return true; + return false; +} + + +// Class Reader +// ////////////////////////////////////////////////////////////////// + +Reader::Reader() + : errors_(), + document_(), + begin_(), + end_(), + current_(), + lastValueEnd_(), + lastValue_(), + commentsBefore_(), + features_( Features::all() ), + collectComments_() +{ +} + + +Reader::Reader( const Features &features ) + : errors_(), + document_(), + begin_(), + end_(), + current_(), + lastValueEnd_(), + lastValue_(), + commentsBefore_(), + features_( features ), + collectComments_() +{ +} + + +bool +Reader::parse( const std::string &document, + Value &root, + bool collectComments ) +{ + document_ = document; + const char *begin = document_.c_str(); + const char *end = begin + document_.length(); + return parse( begin, end, root, collectComments ); +} + + +bool +Reader::parse( std::istream& sin, + Value &root, + bool collectComments ) +{ + //std::istream_iterator<char> begin(sin); + //std::istream_iterator<char> end; + // Those would allow streamed input from a file, if parse() were a + // template function. + + // Since std::string is reference-counted, this at least does not + // create an extra copy. + std::string doc; + std::getline(sin, doc, (char)EOF); + return parse( doc, root, collectComments ); +} + +bool +Reader::parse( const char *beginDoc, const char *endDoc, + Value &root, + bool collectComments ) +{ + if ( !features_.allowComments_ ) + { + collectComments = false; + } + + begin_ = beginDoc; + end_ = endDoc; + collectComments_ = collectComments; + current_ = begin_; + lastValueEnd_ = 0; + lastValue_ = 0; + commentsBefore_ = ""; + errors_.clear(); + while ( !nodes_.empty() ) + nodes_.pop(); + nodes_.push( &root ); + + bool successful = readValue(); + Token token; + skipCommentTokens( token ); + if ( collectComments_ && !commentsBefore_.empty() ) + root.setComment( commentsBefore_, commentAfter ); + if ( features_.strictRoot_ ) + { + if ( !root.isArray() && !root.isObject() ) + { + // Set error location to start of doc, ideally should be first token found in doc + token.type_ = tokenError; + token.start_ = beginDoc; + token.end_ = endDoc; + addError( "A valid JSON document must be either an array or an object value.", + token ); + return false; + } + } + return successful; +} + + +bool +Reader::readValue() +{ + Token token; + skipCommentTokens( token ); + bool successful = true; + + if ( collectComments_ && !commentsBefore_.empty() ) + { + currentValue().setComment( commentsBefore_, commentBefore ); + commentsBefore_ = ""; + } + + + switch ( token.type_ ) + { + case tokenObjectBegin: + successful = readObject( token ); + break; + case tokenArrayBegin: + successful = readArray( token ); + break; + case tokenNumber: + successful = decodeNumber( token ); + break; + case tokenString: + successful = decodeString( token ); + break; + case tokenTrue: + currentValue() = true; + break; + case tokenFalse: + currentValue() = false; + break; + case tokenNull: + currentValue() = Value(); + break; + default: + return addError( "Syntax error: value, object or array expected.", token ); + } + + if ( collectComments_ ) + { + lastValueEnd_ = current_; + lastValue_ = ¤tValue(); + } + + return successful; +} + + +void +Reader::skipCommentTokens( Token &token ) +{ + if ( features_.allowComments_ ) + { + do + { + readToken( token ); + } + while ( token.type_ == tokenComment ); + } + else + { + readToken( token ); + } +} + + +bool +Reader::expectToken( TokenType type, Token &token, const char *message ) +{ + readToken( token ); + if ( token.type_ != type ) + return addError( message, token ); + return true; +} + + +bool +Reader::readToken( Token &token ) +{ + skipSpaces(); + token.start_ = current_; + Char c = getNextChar(); + bool ok = true; + switch ( c ) + { + case '{': + token.type_ = tokenObjectBegin; + break; + case '}': + token.type_ = tokenObjectEnd; + break; + case '[': + token.type_ = tokenArrayBegin; + break; + case ']': + token.type_ = tokenArrayEnd; + break; + case '"': + token.type_ = tokenString; + ok = readString(); + break; + case '/': + token.type_ = tokenComment; + ok = readComment(); + break; + case '0': + case '1': + case '2': + case '3': + case '4': + case '5': + case '6': + case '7': + case '8': + case '9': + case '-': + token.type_ = tokenNumber; + readNumber(); + break; + case 't': + token.type_ = tokenTrue; + ok = match( "rue", 3 ); + break; + case 'f': + token.type_ = tokenFalse; + ok = match( "alse", 4 ); + break; + case 'n': + token.type_ = tokenNull; + ok = match( "ull", 3 ); + break; + case ',': + token.type_ = tokenArraySeparator; + break; + case ':': + token.type_ = tokenMemberSeparator; + break; + case 0: + token.type_ = tokenEndOfStream; + break; + default: + ok = false; + break; + } + if ( !ok ) + token.type_ = tokenError; + token.end_ = current_; + return true; +} + + +void +Reader::skipSpaces() +{ + while ( current_ != end_ ) + { + Char c = *current_; + if ( c == ' ' || c == '\t' || c == '\r' || c == '\n' ) + ++current_; + else + break; + } +} + + +bool +Reader::match( Location pattern, + int patternLength ) +{ + if ( end_ - current_ < patternLength ) + return false; + int index = patternLength; + while ( index-- ) + if ( current_[index] != pattern[index] ) + return false; + current_ += patternLength; + return true; +} + + +bool +Reader::readComment() +{ + Location commentBegin = current_ - 1; + Char c = getNextChar(); + bool successful = false; + if ( c == '*' ) + successful = readCStyleComment(); + else if ( c == '/' ) + successful = readCppStyleComment(); + if ( !successful ) + return false; + + if ( collectComments_ ) + { + CommentPlacement placement = commentBefore; + if ( lastValueEnd_ && !containsNewLine( lastValueEnd_, commentBegin ) ) + { + if ( c != '*' || !containsNewLine( commentBegin, current_ ) ) + placement = commentAfterOnSameLine; + } + + addComment( commentBegin, current_, placement ); + } + return true; +} + + +void +Reader::addComment( Location begin, + Location end, + CommentPlacement placement ) +{ + assert( collectComments_ ); + if ( placement == commentAfterOnSameLine ) + { + assert( lastValue_ != 0 ); + lastValue_->setComment( std::string( begin, end ), placement ); + } + else + { + if ( !commentsBefore_.empty() ) + commentsBefore_ += "\n"; + commentsBefore_ += std::string( begin, end ); + } +} + + +bool +Reader::readCStyleComment() +{ + while ( current_ != end_ ) + { + Char c = getNextChar(); + if ( c == '*' && *current_ == '/' ) + break; + } + return getNextChar() == '/'; +} + + +bool +Reader::readCppStyleComment() +{ + while ( current_ != end_ ) + { + Char c = getNextChar(); + if ( c == '\r' || c == '\n' ) + break; + } + return true; +} + + +void +Reader::readNumber() +{ + while ( current_ != end_ ) + { + if ( !(*current_ >= '0' && *current_ <= '9') && + !in( *current_, '.', 'e', 'E', '+', '-' ) ) + break; + ++current_; + } +} + +bool +Reader::readString() +{ + Char c = 0; + while ( current_ != end_ ) + { + c = getNextChar(); + if ( c == '\\' ) + getNextChar(); + else if ( c == '"' ) + break; + } + return c == '"'; +} + + +bool +Reader::readObject( Token &/*tokenStart*/ ) +{ + Token tokenName; + std::string name; + currentValue() = Value( objectValue ); + while ( readToken( tokenName ) ) + { + bool initialTokenOk = true; + while ( tokenName.type_ == tokenComment && initialTokenOk ) + initialTokenOk = readToken( tokenName ); + if ( !initialTokenOk ) + break; + if ( tokenName.type_ == tokenObjectEnd && name.empty() ) // empty object + return true; + if ( tokenName.type_ != tokenString ) + break; + + name = ""; + if ( !decodeString( tokenName, name ) ) + return recoverFromError( tokenObjectEnd ); + + Token colon; + if ( !readToken( colon ) || colon.type_ != tokenMemberSeparator ) + { + return addErrorAndRecover( "Missing ':' after object member name", + colon, + tokenObjectEnd ); + } + Value &value = currentValue()[ name ]; + nodes_.push( &value ); + bool ok = readValue(); + nodes_.pop(); + if ( !ok ) // error already set + return recoverFromError( tokenObjectEnd ); + + Token comma; + if ( !readToken( comma ) + || ( comma.type_ != tokenObjectEnd && + comma.type_ != tokenArraySeparator && + comma.type_ != tokenComment ) ) + { + return addErrorAndRecover( "Missing ',' or '}' in object declaration", + comma, + tokenObjectEnd ); + } + bool finalizeTokenOk = true; + while ( comma.type_ == tokenComment && + finalizeTokenOk ) + finalizeTokenOk = readToken( comma ); + if ( comma.type_ == tokenObjectEnd ) + return true; + } + return addErrorAndRecover( "Missing '}' or object member name", + tokenName, + tokenObjectEnd ); +} + + +bool +Reader::readArray( Token &/*tokenStart*/ ) +{ + currentValue() = Value( arrayValue ); + skipSpaces(); + if ( *current_ == ']' ) // empty array + { + Token endArray; + readToken( endArray ); + return true; + } + int index = 0; + for (;;) + { + Value &value = currentValue()[ index++ ]; + nodes_.push( &value ); + bool ok = readValue(); + nodes_.pop(); + if ( !ok ) // error already set + return recoverFromError( tokenArrayEnd ); + + Token token; + // Accept Comment after last item in the array. + ok = readToken( token ); + while ( token.type_ == tokenComment && ok ) + { + ok = readToken( token ); + } + bool badTokenType = ( token.type_ != tokenArraySeparator && + token.type_ != tokenArrayEnd ); + if ( !ok || badTokenType ) + { + return addErrorAndRecover( "Missing ',' or ']' in array declaration", + token, + tokenArrayEnd ); + } + if ( token.type_ == tokenArrayEnd ) + break; + } + return true; +} + + +bool +Reader::decodeNumber( Token &token ) +{ + bool isDouble = false; + for ( Location inspect = token.start_; inspect != token.end_; ++inspect ) + { + isDouble = isDouble + || in( *inspect, '.', 'e', 'E', '+' ) + || ( *inspect == '-' && inspect != token.start_ ); + } + if ( isDouble ) + return decodeDouble( token ); + // Attempts to parse the number as an integer. If the number is + // larger than the maximum supported value of an integer then + // we decode the number as a double. + Location current = token.start_; + bool isNegative = *current == '-'; + if ( isNegative ) + ++current; + Value::LargestUInt maxIntegerValue = isNegative ? Value::LargestUInt(-Value::minLargestInt) + : Value::maxLargestUInt; + Value::LargestUInt threshold = maxIntegerValue / 10; + Value::LargestUInt value = 0; + while ( current < token.end_ ) + { + Char c = *current++; + if ( c < '0' || c > '9' ) + return addError( "'" + std::string( token.start_, token.end_ ) + "' is not a number.", token ); + Value::UInt digit(c - '0'); + if ( value >= threshold ) + { + // We've hit or exceeded the max value divided by 10 (rounded down). If + // a) we've only just touched the limit, b) this is the last digit, and + // c) it's small enough to fit in that rounding delta, we're okay. + // Otherwise treat this number as a double to avoid overflow. + if (value > threshold || + current != token.end_ || + digit > maxIntegerValue % 10) + { + return decodeDouble( token ); + } + } + value = value * 10 + digit; + } + if ( isNegative ) + currentValue() = -Value::LargestInt( value ); + else if ( value <= Value::LargestUInt(Value::maxInt) ) + currentValue() = Value::LargestInt( value ); + else + currentValue() = value; + return true; +} + + +bool +Reader::decodeDouble( Token &token ) +{ + double value = 0; + const int bufferSize = 32; + int count; + int length = int(token.end_ - token.start_); + + // Sanity check to avoid buffer overflow exploits. + if (length < 0) { + return addError( "Unable to parse token length", token ); + } + + // Avoid using a string constant for the format control string given to + // sscanf, as this can cause hard to debug crashes on OS X. See here for more + // info: + // + // http://developer.apple.com/library/mac/#DOCUMENTATION/DeveloperTools/gcc-4.0.1/gcc/Incompatibilities.html + char format[] = "%lf"; + + if ( length <= bufferSize ) + { + Char buffer[bufferSize+1]; + memcpy( buffer, token.start_, length ); + buffer[length] = 0; + count = sscanf( buffer, format, &value ); + } + else + { + std::string buffer( token.start_, token.end_ ); + count = sscanf( buffer.c_str(), format, &value ); + } + + if ( count != 1 ) + return addError( "'" + std::string( token.start_, token.end_ ) + "' is not a number.", token ); + currentValue() = value; + return true; +} + + +bool +Reader::decodeString( Token &token ) +{ + std::string decoded; + if ( !decodeString( token, decoded ) ) + return false; + currentValue() = decoded; + return true; +} + + +bool +Reader::decodeString( Token &token, std::string &decoded ) +{ + decoded.reserve( token.end_ - token.start_ - 2 ); + Location current = token.start_ + 1; // skip '"' + Location end = token.end_ - 1; // do not include '"' + while ( current != end ) + { + Char c = *current++; + if ( c == '"' ) + break; + else if ( c == '\\' ) + { + if ( current == end ) + return addError( "Empty escape sequence in string", token, current ); + Char escape = *current++; + switch ( escape ) + { + case '"': decoded += '"'; break; + case '/': decoded += '/'; break; + case '\\': decoded += '\\'; break; + case 'b': decoded += '\b'; break; + case 'f': decoded += '\f'; break; + case 'n': decoded += '\n'; break; + case 'r': decoded += '\r'; break; + case 't': decoded += '\t'; break; + case 'u': + { + unsigned int unicode; + if ( !decodeUnicodeCodePoint( token, current, end, unicode ) ) + return false; + decoded += codePointToUTF8(unicode); + } + break; + default: + return addError( "Bad escape sequence in string", token, current ); + } + } + else + { + decoded += c; + } + } + return true; +} + +bool +Reader::decodeUnicodeCodePoint( Token &token, + Location ¤t, + Location end, + unsigned int &unicode ) +{ + + if ( !decodeUnicodeEscapeSequence( token, current, end, unicode ) ) + return false; + if (unicode >= 0xD800 && unicode <= 0xDBFF) + { + // surrogate pairs + if (end - current < 6) + return addError( "additional six characters expected to parse unicode surrogate pair.", token, current ); + unsigned int surrogatePair; + if (*(current++) == '\\' && *(current++)== 'u') + { + if (decodeUnicodeEscapeSequence( token, current, end, surrogatePair )) + { + unicode = 0x10000 + ((unicode & 0x3FF) << 10) + (surrogatePair & 0x3FF); + } + else + return false; + } + else + return addError( "expecting another \\u token to begin the second half of a unicode surrogate pair", token, current ); + } + return true; +} + +bool +Reader::decodeUnicodeEscapeSequence( Token &token, + Location ¤t, + Location end, + unsigned int &unicode ) +{ + if ( end - current < 4 ) + return addError( "Bad unicode escape sequence in string: four digits expected.", token, current ); + unicode = 0; + for ( int index =0; index < 4; ++index ) + { + Char c = *current++; + unicode *= 16; + if ( c >= '0' && c <= '9' ) + unicode += c - '0'; + else if ( c >= 'a' && c <= 'f' ) + unicode += c - 'a' + 10; + else if ( c >= 'A' && c <= 'F' ) + unicode += c - 'A' + 10; + else + return addError( "Bad unicode escape sequence in string: hexadecimal digit expected.", token, current ); + } + return true; +} + + +bool +Reader::addError( const std::string &message, + Token &token, + Location extra ) +{ + ErrorInfo info; + info.token_ = token; + info.message_ = message; + info.extra_ = extra; + errors_.push_back( info ); + return false; +} + + +bool +Reader::recoverFromError( TokenType skipUntilToken ) +{ + int errorCount = int(errors_.size()); + Token skip; + for (;;) + { + if ( !readToken(skip) ) + errors_.resize( errorCount ); // discard errors caused by recovery + if ( skip.type_ == skipUntilToken || skip.type_ == tokenEndOfStream ) + break; + } + errors_.resize( errorCount ); + return false; +} + + +bool +Reader::addErrorAndRecover( const std::string &message, + Token &token, + TokenType skipUntilToken ) +{ + addError( message, token ); + return recoverFromError( skipUntilToken ); +} + + +Value & +Reader::currentValue() +{ + return *(nodes_.top()); +} + + +Reader::Char +Reader::getNextChar() +{ + if ( current_ == end_ ) + return 0; + return *current_++; +} + + +void +Reader::getLocationLineAndColumn( Location location, + int &line, + int &column ) const +{ + Location current = begin_; + Location lastLineStart = current; + line = 0; + while ( current < location && current != end_ ) + { + Char c = *current++; + if ( c == '\r' ) + { + if ( *current == '\n' ) + ++current; + lastLineStart = current; + ++line; + } + else if ( c == '\n' ) + { + lastLineStart = current; + ++line; + } + } + // column & line start at 1 + column = int(location - lastLineStart) + 1; + ++line; +} + + +std::string +Reader::getLocationLineAndColumn( Location location ) const +{ + int line, column; + getLocationLineAndColumn( location, line, column ); + char buffer[18+16+16+1]; + sprintf( buffer, "Line %d, Column %d", line, column ); + return buffer; +} + + +// Deprecated. Preserved for backward compatibility +std::string +Reader::getFormatedErrorMessages() const +{ + return getFormattedErrorMessages(); +} + + +std::string +Reader::getFormattedErrorMessages() const +{ + std::string formattedMessage; + for ( Errors::const_iterator itError = errors_.begin(); + itError != errors_.end(); + ++itError ) + { + const ErrorInfo &error = *itError; + formattedMessage += "* " + getLocationLineAndColumn( error.token_.start_ ) + "\n"; + formattedMessage += " " + error.message_ + "\n"; + if ( error.extra_ ) + formattedMessage += "See " + getLocationLineAndColumn( error.extra_ ) + " for detail.\n"; + } + return formattedMessage; +} + + +std::istream& operator>>( std::istream &sin, Value &root ) +{ + Json::Reader reader; + bool ok = reader.parse(sin, root, true); + if (!ok) JSON_FAIL_MESSAGE(reader.getFormattedErrorMessages()); + return sin; +} + + +} // namespace Json
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/third_party/json/json_tool.h Thu Aug 25 11:05:55 2011 +0100 @@ -0,0 +1,93 @@ +// Copyright 2007-2010 Baptiste Lepilleur +// Distributed under MIT license, or public domain if desired and +// recognized in your jurisdiction. +// See file LICENSE for detail or copy at http://jsoncpp.sourceforge.net/LICENSE + +#ifndef LIB_JSONCPP_JSON_TOOL_H_INCLUDED +# define LIB_JSONCPP_JSON_TOOL_H_INCLUDED + +/* This header provides common string manipulation support, such as UTF-8, + * portable conversion from/to string... + * + * It is an internal header that must not be exposed. + */ + +namespace Json { + +/// Converts a unicode code-point to UTF-8. +static inline std::string +codePointToUTF8(unsigned int cp) +{ + std::string result; + + // based on description from http://en.wikipedia.org/wiki/UTF-8 + + if (cp <= 0x7f) + { + result.resize(1); + result[0] = static_cast<char>(cp); + } + else if (cp <= 0x7FF) + { + result.resize(2); + result[1] = static_cast<char>(0x80 | (0x3f & cp)); + result[0] = static_cast<char>(0xC0 | (0x1f & (cp >> 6))); + } + else if (cp <= 0xFFFF) + { + result.resize(3); + result[2] = static_cast<char>(0x80 | (0x3f & cp)); + result[1] = 0x80 | static_cast<char>((0x3f & (cp >> 6))); + result[0] = 0xE0 | static_cast<char>((0xf & (cp >> 12))); + } + else if (cp <= 0x10FFFF) + { + result.resize(4); + result[3] = static_cast<char>(0x80 | (0x3f & cp)); + result[2] = static_cast<char>(0x80 | (0x3f & (cp >> 6))); + result[1] = static_cast<char>(0x80 | (0x3f & (cp >> 12))); + result[0] = static_cast<char>(0xF0 | (0x7 & (cp >> 18))); + } + + return result; +} + + +/// Returns true if ch is a control character (in range [0,32[). +static inline bool +isControlCharacter(char ch) +{ + return ch > 0 && ch <= 0x1F; +} + + +enum { + /// Constant that specify the size of the buffer that must be passed to uintToString. + uintToStringBufferSize = 3*sizeof(LargestUInt)+1 +}; + +// Defines a char buffer for use with uintToString(). +typedef char UIntToStringBuffer[uintToStringBufferSize]; + + +/** Converts an unsigned integer to string. + * @param value Unsigned interger to convert to string + * @param current Input/Output string buffer. + * Must have at least uintToStringBufferSize chars free. + */ +static inline void +uintToString( LargestUInt value, + char *¤t ) +{ + *--current = 0; + do + { + *--current = char(value % 10) + '0'; + value /= 10; + } + while ( value != 0 ); +} + +} // namespace Json { + +#endif // LIB_JSONCPP_JSON_TOOL_H_INCLUDED
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/third_party/json/json_value.cpp Thu Aug 25 11:05:55 2011 +0100 @@ -0,0 +1,1921 @@ +// Copyright 2011 Baptiste Lepilleur +// Distributed under MIT license, or public domain if desired and +// recognized in your jurisdiction. +// See file LICENSE for detail or copy at http://jsoncpp.sourceforge.net/LICENSE + +#if !defined(JSON_IS_AMALGAMATION) +# include <json/assertions.h> +# include <json/value.h> +# include <json/writer.h> +# ifndef JSON_USE_SIMPLE_INTERNAL_ALLOCATOR +# include "json_batchallocator.h" +# endif // #ifndef JSON_USE_SIMPLE_INTERNAL_ALLOCATOR +#endif // if !defined(JSON_IS_AMALGAMATION) +#include <math.h> +#include <iostream> +#include <sstream> +#include <utility> +#include <stdexcept> +#include <cstring> +#include <cassert> +#ifdef JSON_USE_CPPTL +# include <cpptl/conststring.h> +#endif +#include <cstddef> // size_t + +#define JSON_ASSERT_UNREACHABLE assert( false ) + +namespace Json { + +const Value Value::null; +const Int Value::minInt = Int( ~(UInt(-1)/2) ); +const Int Value::maxInt = Int( UInt(-1)/2 ); +const UInt Value::maxUInt = UInt(-1); +# if defined(JSON_HAS_INT64) +const Int64 Value::minInt64 = Int64( ~(UInt64(-1)/2) ); +const Int64 Value::maxInt64 = Int64( UInt64(-1)/2 ); +const UInt64 Value::maxUInt64 = UInt64(-1); +// The constant is hard-coded because some compiler have trouble +// converting Value::maxUInt64 to a double correctly (AIX/xlC). +// Assumes that UInt64 is a 64 bits integer. +static const double maxUInt64AsDouble = 18446744073709551615.0; +#endif // defined(JSON_HAS_INT64) +const LargestInt Value::minLargestInt = LargestInt( ~(LargestUInt(-1)/2) ); +const LargestInt Value::maxLargestInt = LargestInt( LargestUInt(-1)/2 ); +const LargestUInt Value::maxLargestUInt = LargestUInt(-1); + + +/// Unknown size marker +static const unsigned int unknown = (unsigned)-1; + +#if !defined(JSON_USE_INT64_DOUBLE_CONVERSION) +template <typename T, typename U> +static inline bool InRange(double d, T min, U max) { + return d >= min && d <= max; +} +#else // if !defined(JSON_USE_INT64_DOUBLE_CONVERSION) +static inline double integerToDouble( Json::UInt64 value ) +{ + return static_cast<double>( Int64(value/2) ) * 2.0 + Int64(value & 1); +} + +template<typename T> +static inline double integerToDouble( T value ) +{ + return static_cast<double>( value ); +} + +template <typename T, typename U> +static inline bool InRange(double d, T min, U max) { + return d >= integerToDouble(min) && d <= integerToDouble(max); +} +#endif // if !defined(JSON_USE_INT64_DOUBLE_CONVERSION) + + +/** Duplicates the specified string value. + * @param value Pointer to the string to duplicate. Must be zero-terminated if + * length is "unknown". + * @param length Length of the value. if equals to unknown, then it will be + * computed using strlen(value). + * @return Pointer on the duplicate instance of string. + */ +static inline char * +duplicateStringValue( const char *value, + unsigned int length = unknown ) +{ + if ( length == unknown ) + length = (unsigned int)strlen(value); + + // Avoid an integer overflow in the call to malloc below by limiting length + // to a sane value. + if (length >= (unsigned)Value::maxInt) + length = Value::maxInt - 1; + + char *newString = static_cast<char *>( malloc( length + 1 ) ); + JSON_ASSERT_MESSAGE( newString != 0, "Failed to allocate string value buffer" ); + memcpy( newString, value, length ); + newString[length] = 0; + return newString; +} + + +/** Free the string duplicated by duplicateStringValue(). + */ +static inline void +releaseStringValue( char *value ) +{ + if ( value ) + free( value ); +} + +} // namespace Json + + +// ////////////////////////////////////////////////////////////////// +// ////////////////////////////////////////////////////////////////// +// ////////////////////////////////////////////////////////////////// +// ValueInternals... +// ////////////////////////////////////////////////////////////////// +// ////////////////////////////////////////////////////////////////// +// ////////////////////////////////////////////////////////////////// +#if !defined(JSON_IS_AMALGAMATION) +# ifdef JSON_VALUE_USE_INTERNAL_MAP +# include "json_internalarray.inl" +# include "json_internalmap.inl" +# endif // JSON_VALUE_USE_INTERNAL_MAP + +# include "json_valueiterator.inl" +#endif // if !defined(JSON_IS_AMALGAMATION) + +namespace Json { + +// ////////////////////////////////////////////////////////////////// +// ////////////////////////////////////////////////////////////////// +// ////////////////////////////////////////////////////////////////// +// class Value::CommentInfo +// ////////////////////////////////////////////////////////////////// +// ////////////////////////////////////////////////////////////////// +// ////////////////////////////////////////////////////////////////// + + +Value::CommentInfo::CommentInfo() + : comment_( 0 ) +{ +} + +Value::CommentInfo::~CommentInfo() +{ + if ( comment_ ) + releaseStringValue( comment_ ); +} + + +void +Value::CommentInfo::setComment( const char *text ) +{ + if ( comment_ ) + releaseStringValue( comment_ ); + JSON_ASSERT( text != 0 ); + JSON_ASSERT_MESSAGE( text[0]=='\0' || text[0]=='/', "Comments must start with /"); + // It seems that /**/ style comments are acceptable as well. + comment_ = duplicateStringValue( text ); +} + + +// ////////////////////////////////////////////////////////////////// +// ////////////////////////////////////////////////////////////////// +// ////////////////////////////////////////////////////////////////// +// class Value::CZString +// ////////////////////////////////////////////////////////////////// +// ////////////////////////////////////////////////////////////////// +// ////////////////////////////////////////////////////////////////// +# ifndef JSON_VALUE_USE_INTERNAL_MAP + +// Notes: index_ indicates if the string was allocated when +// a string is stored. + +Value::CZString::CZString( ArrayIndex index ) + : cstr_( 0 ) + , index_( index ) +{ +} + +Value::CZString::CZString( const char *cstr, DuplicationPolicy allocate ) + : cstr_( allocate == duplicate ? duplicateStringValue(cstr) + : cstr ) + , index_( allocate ) +{ +} + +Value::CZString::CZString( const CZString &other ) +: cstr_( other.index_ != noDuplication && other.cstr_ != 0 + ? duplicateStringValue( other.cstr_ ) + : other.cstr_ ) + , index_( other.cstr_ ? (other.index_ == noDuplication ? noDuplication : duplicate) + : other.index_ ) +{ +} + +Value::CZString::~CZString() +{ + if ( cstr_ && index_ == duplicate ) + releaseStringValue( const_cast<char *>( cstr_ ) ); +} + +void +Value::CZString::swap( CZString &other ) +{ + std::swap( cstr_, other.cstr_ ); + std::swap( index_, other.index_ ); +} + +Value::CZString & +Value::CZString::operator =( const CZString &other ) +{ + CZString temp( other ); + swap( temp ); + return *this; +} + +bool +Value::CZString::operator<( const CZString &other ) const +{ + if ( cstr_ ) + return strcmp( cstr_, other.cstr_ ) < 0; + return index_ < other.index_; +} + +bool +Value::CZString::operator==( const CZString &other ) const +{ + if ( cstr_ ) + return strcmp( cstr_, other.cstr_ ) == 0; + return index_ == other.index_; +} + + +ArrayIndex +Value::CZString::index() const +{ + return index_; +} + + +const char * +Value::CZString::c_str() const +{ + return cstr_; +} + +bool +Value::CZString::isStaticString() const +{ + return index_ == noDuplication; +} + +#endif // ifndef JSON_VALUE_USE_INTERNAL_MAP + + +// ////////////////////////////////////////////////////////////////// +// ////////////////////////////////////////////////////////////////// +// ////////////////////////////////////////////////////////////////// +// class Value::Value +// ////////////////////////////////////////////////////////////////// +// ////////////////////////////////////////////////////////////////// +// ////////////////////////////////////////////////////////////////// + +/*! \internal Default constructor initialization must be equivalent to: + * memset( this, 0, sizeof(Value) ) + * This optimization is used in ValueInternalMap fast allocator. + */ +Value::Value( ValueType type ) + : type_( type ) + , allocated_( false ) +# ifdef JSON_VALUE_USE_INTERNAL_MAP + , itemIsUsed_( 0 ) +#endif + , comments_( 0 ) +{ + switch ( type ) + { + case nullValue: + break; + case intValue: + case uintValue: + value_.int_ = 0; + break; + case realValue: + value_.real_ = 0.0; + break; + case stringValue: + value_.string_ = 0; + break; +#ifndef JSON_VALUE_USE_INTERNAL_MAP + case arrayValue: + case objectValue: + value_.map_ = new ObjectValues(); + break; +#else + case arrayValue: + value_.array_ = arrayAllocator()->newArray(); + break; + case objectValue: + value_.map_ = mapAllocator()->newMap(); + break; +#endif + case booleanValue: + value_.bool_ = false; + break; + default: + JSON_ASSERT_UNREACHABLE; + } +} + + +Value::Value( UInt value ) + : type_( uintValue ) + , allocated_( false ) +# ifdef JSON_VALUE_USE_INTERNAL_MAP + , itemIsUsed_( 0 ) +#endif + , comments_( 0 ) +{ + value_.uint_ = value; +} + +Value::Value( Int value ) + : type_( intValue ) + , allocated_( false ) +# ifdef JSON_VALUE_USE_INTERNAL_MAP + , itemIsUsed_( 0 ) +#endif + , comments_( 0 ) +{ + value_.int_ = value; +} + + +# if defined(JSON_HAS_INT64) +Value::Value( Int64 value ) + : type_( intValue ) + , allocated_( false ) +# ifdef JSON_VALUE_USE_INTERNAL_MAP + , itemIsUsed_( 0 ) +#endif + , comments_( 0 ) +{ + value_.int_ = value; +} + + +Value::Value( UInt64 value ) + : type_( uintValue ) + , allocated_( false ) +# ifdef JSON_VALUE_USE_INTERNAL_MAP + , itemIsUsed_( 0 ) +#endif + , comments_( 0 ) +{ + value_.uint_ = value; +} +#endif // defined(JSON_HAS_INT64) + +Value::Value( double value ) + : type_( realValue ) + , allocated_( false ) +# ifdef JSON_VALUE_USE_INTERNAL_MAP + , itemIsUsed_( 0 ) +#endif + , comments_( 0 ) +{ + value_.real_ = value; +} + +Value::Value( const char *value ) + : type_( stringValue ) + , allocated_( true ) +# ifdef JSON_VALUE_USE_INTERNAL_MAP + , itemIsUsed_( 0 ) +#endif + , comments_( 0 ) +{ + value_.string_ = duplicateStringValue( value ); +} + + +Value::Value( const char *beginValue, + const char *endValue ) + : type_( stringValue ) + , allocated_( true ) +# ifdef JSON_VALUE_USE_INTERNAL_MAP + , itemIsUsed_( 0 ) +#endif + , comments_( 0 ) +{ + value_.string_ = duplicateStringValue( beginValue, + (unsigned int)(endValue - beginValue) ); +} + + +Value::Value( const std::string &value ) + : type_( stringValue ) + , allocated_( true ) +# ifdef JSON_VALUE_USE_INTERNAL_MAP + , itemIsUsed_( 0 ) +#endif + , comments_( 0 ) +{ + value_.string_ = duplicateStringValue( value.c_str(), + (unsigned int)value.length() ); + +} + +Value::Value( const StaticString &value ) + : type_( stringValue ) + , allocated_( false ) +# ifdef JSON_VALUE_USE_INTERNAL_MAP + , itemIsUsed_( 0 ) +#endif + , comments_( 0 ) +{ + value_.string_ = const_cast<char *>( value.c_str() ); +} + + +# ifdef JSON_USE_CPPTL +Value::Value( const CppTL::ConstString &value ) + : type_( stringValue ) + , allocated_( true ) +# ifdef JSON_VALUE_USE_INTERNAL_MAP + , itemIsUsed_( 0 ) +#endif + , comments_( 0 ) +{ + value_.string_ = duplicateStringValue( value, value.length() ); +} +# endif + +Value::Value( bool value ) + : type_( booleanValue ) + , allocated_( false ) +# ifdef JSON_VALUE_USE_INTERNAL_MAP + , itemIsUsed_( 0 ) +#endif + , comments_( 0 ) +{ + value_.bool_ = value; +} + + +Value::Value( const Value &other ) + : type_( other.type_ ) + , allocated_( false ) +# ifdef JSON_VALUE_USE_INTERNAL_MAP + , itemIsUsed_( 0 ) +#endif + , comments_( 0 ) +{ + switch ( type_ ) + { + case nullValue: + case intValue: + case uintValue: + case realValue: + case booleanValue: + value_ = other.value_; + break; + case stringValue: + if ( other.value_.string_ ) + { + value_.string_ = duplicateStringValue( other.value_.string_ ); + allocated_ = true; + } + else + value_.string_ = 0; + break; +#ifndef JSON_VALUE_USE_INTERNAL_MAP + case arrayValue: + case objectValue: + value_.map_ = new ObjectValues( *other.value_.map_ ); + break; +#else + case arrayValue: + value_.array_ = arrayAllocator()->newArrayCopy( *other.value_.array_ ); + break; + case objectValue: + value_.map_ = mapAllocator()->newMapCopy( *other.value_.map_ ); + break; +#endif + default: + JSON_ASSERT_UNREACHABLE; + } + if ( other.comments_ ) + { + comments_ = new CommentInfo[numberOfCommentPlacement]; + for ( int comment =0; comment < numberOfCommentPlacement; ++comment ) + { + const CommentInfo &otherComment = other.comments_[comment]; + if ( otherComment.comment_ ) + comments_[comment].setComment( otherComment.comment_ ); + } + } +} + + +Value::~Value() +{ + switch ( type_ ) + { + case nullValue: + case intValue: + case uintValue: + case realValue: + case booleanValue: + break; + case stringValue: + if ( allocated_ ) + releaseStringValue( value_.string_ ); + break; +#ifndef JSON_VALUE_USE_INTERNAL_MAP + case arrayValue: + case objectValue: + delete value_.map_; + break; +#else + case arrayValue: + arrayAllocator()->destructArray( value_.array_ ); + break; + case objectValue: + mapAllocator()->destructMap( value_.map_ ); + break; +#endif + default: + JSON_ASSERT_UNREACHABLE; + } + + if ( comments_ ) + delete[] comments_; +} + +Value & +Value::operator=( const Value &other ) +{ + Value temp( other ); + swap( temp ); + return *this; +} + +void +Value::swap( Value &other ) +{ + ValueType temp = type_; + type_ = other.type_; + other.type_ = temp; + std::swap( value_, other.value_ ); + int temp2 = allocated_; + allocated_ = other.allocated_; + other.allocated_ = temp2; +} + +ValueType +Value::type() const +{ + return type_; +} + + +int +Value::compare( const Value &other ) const +{ + if ( *this < other ) + return -1; + if ( *this > other ) + return 1; + return 0; +} + + +bool +Value::operator <( const Value &other ) const +{ + int typeDelta = type_ - other.type_; + if ( typeDelta ) + return typeDelta < 0 ? true : false; + switch ( type_ ) + { + case nullValue: + return false; + case intValue: + return value_.int_ < other.value_.int_; + case uintValue: + return value_.uint_ < other.value_.uint_; + case realValue: + return value_.real_ < other.value_.real_; + case booleanValue: + return value_.bool_ < other.value_.bool_; + case stringValue: + return ( value_.string_ == 0 && other.value_.string_ ) + || ( other.value_.string_ + && value_.string_ + && strcmp( value_.string_, other.value_.string_ ) < 0 ); +#ifndef JSON_VALUE_USE_INTERNAL_MAP + case arrayValue: + case objectValue: + { + int delta = int( value_.map_->size() - other.value_.map_->size() ); + if ( delta ) + return delta < 0; + return (*value_.map_) < (*other.value_.map_); + } +#else + case arrayValue: + return value_.array_->compare( *(other.value_.array_) ) < 0; + case objectValue: + return value_.map_->compare( *(other.value_.map_) ) < 0; +#endif + default: + JSON_ASSERT_UNREACHABLE; + } + return false; // unreachable +} + +bool +Value::operator <=( const Value &other ) const +{ + return !(other < *this); +} + +bool +Value::operator >=( const Value &other ) const +{ + return !(*this < other); +} + +bool +Value::operator >( const Value &other ) const +{ + return other < *this; +} + +bool +Value::operator ==( const Value &other ) const +{ + //if ( type_ != other.type_ ) + // GCC 2.95.3 says: + // attempt to take address of bit-field structure member `Json::Value::type_' + // Beats me, but a temp solves the problem. + int temp = other.type_; + if ( type_ != temp ) + return false; + switch ( type_ ) + { + case nullValue: + return true; + case intValue: + return value_.int_ == other.value_.int_; + case uintValue: + return value_.uint_ == other.value_.uint_; + case realValue: + return value_.real_ == other.value_.real_; + case booleanValue: + return value_.bool_ == other.value_.bool_; + case stringValue: + return ( value_.string_ == other.value_.string_ ) + || ( other.value_.string_ + && value_.string_ + && strcmp( value_.string_, other.value_.string_ ) == 0 ); +#ifndef JSON_VALUE_USE_INTERNAL_MAP + case arrayValue: + case objectValue: + return value_.map_->size() == other.value_.map_->size() + && (*value_.map_) == (*other.value_.map_); +#else + case arrayValue: + return value_.array_->compare( *(other.value_.array_) ) == 0; + case objectValue: + return value_.map_->compare( *(other.value_.map_) ) == 0; +#endif + default: + JSON_ASSERT_UNREACHABLE; + } + return false; // unreachable +} + +bool +Value::operator !=( const Value &other ) const +{ + return !( *this == other ); +} + +const char * +Value::asCString() const +{ + JSON_ASSERT( type_ == stringValue ); + return value_.string_; +} + + +std::string +Value::asString() const +{ + switch ( type_ ) + { + case nullValue: + return ""; + case stringValue: + return value_.string_ ? value_.string_ : ""; + case booleanValue: + return value_.bool_ ? "true" : "false"; + case intValue: + return valueToString( value_.int_ ); + case uintValue: + return valueToString( value_.uint_ ); + case realValue: + return valueToString( value_.real_ ); + default: + JSON_FAIL_MESSAGE( "Type is not convertible to string" ); + } +} + +# ifdef JSON_USE_CPPTL +CppTL::ConstString +Value::asConstString() const +{ + return CppTL::ConstString( asString().c_str() ); +} +# endif + + +Value::Int +Value::asInt() const +{ + switch ( type_ ) + { + case intValue: + JSON_ASSERT_MESSAGE(isInt(), "LargestInt out of Int range"); + return Int(value_.int_); + case uintValue: + JSON_ASSERT_MESSAGE(isInt(), "LargestUInt out of Int range"); + return Int(value_.uint_); + case realValue: + JSON_ASSERT_MESSAGE(InRange(value_.real_, minInt, maxInt), "double out of Int range"); + return Int(value_.real_); + case nullValue: + return 0; + case booleanValue: + return value_.bool_ ? 1 : 0; + default: + break; + } + JSON_FAIL_MESSAGE("Value is not convertible to Int."); +} + + +Value::UInt +Value::asUInt() const +{ + switch ( type_ ) + { + case intValue: + JSON_ASSERT_MESSAGE(isUInt(), "LargestInt out of UInt range"); + return UInt(value_.int_); + case uintValue: + JSON_ASSERT_MESSAGE(isUInt(), "LargestUInt out of UInt range"); + return UInt(value_.uint_); + case realValue: + JSON_ASSERT_MESSAGE(InRange(value_.real_, 0, maxUInt), "double out of UInt range"); + return UInt( value_.real_ ); + case nullValue: + return 0; + case booleanValue: + return value_.bool_ ? 1 : 0; + default: + break; + } + JSON_FAIL_MESSAGE("Value is not convertible to UInt."); +} + + +# if defined(JSON_HAS_INT64) + +Value::Int64 +Value::asInt64() const +{ + switch ( type_ ) + { + case intValue: + return Int64(value_.int_); + case uintValue: + JSON_ASSERT_MESSAGE(isInt64(), "LargestUInt out of Int64 range"); + return Int64(value_.uint_); + case realValue: + JSON_ASSERT_MESSAGE(InRange(value_.real_, minInt64, maxInt64), "double out of Int64 range"); + return Int64(value_.real_); + case nullValue: + return 0; + case booleanValue: + return value_.bool_ ? 1 : 0; + default: + break; + } + JSON_FAIL_MESSAGE("Value is not convertible to Int64."); +} + + +Value::UInt64 +Value::asUInt64() const +{ + switch ( type_ ) + { + case intValue: + JSON_ASSERT_MESSAGE(isUInt64(), "LargestInt out of UInt64 range"); + return UInt64(value_.int_); + case uintValue: + return UInt64(value_.uint_); + case realValue: + JSON_ASSERT_MESSAGE(InRange(value_.real_, 0, maxUInt64), "double out of UInt64 range"); + return UInt64( value_.real_ ); + case nullValue: + return 0; + case booleanValue: + return value_.bool_ ? 1 : 0; + default: + break; + } + JSON_FAIL_MESSAGE("Value is not convertible to UInt64."); +} +# endif // if defined(JSON_HAS_INT64) + + +LargestInt +Value::asLargestInt() const +{ +#if defined(JSON_NO_INT64) + return asInt(); +#else + return asInt64(); +#endif +} + + +LargestUInt +Value::asLargestUInt() const +{ +#if defined(JSON_NO_INT64) + return asUInt(); +#else + return asUInt64(); +#endif +} + + +double +Value::asDouble() const +{ + switch ( type_ ) + { + case intValue: + return static_cast<double>( value_.int_ ); + case uintValue: +#if !defined(JSON_USE_INT64_DOUBLE_CONVERSION) + return static_cast<double>( value_.uint_ ); +#else // if !defined(JSON_USE_INT64_DOUBLE_CONVERSION) + return integerToDouble( value_.uint_ ); +#endif // if !defined(JSON_USE_INT64_DOUBLE_CONVERSION) + case realValue: + return value_.real_; + case nullValue: + return 0.0; + case booleanValue: + return value_.bool_ ? 1.0 : 0.0; + default: + break; + } + JSON_FAIL_MESSAGE("Value is not convertible to double."); +} + +float +Value::asFloat() const +{ + switch ( type_ ) + { + case intValue: + return static_cast<float>( value_.int_ ); + case uintValue: +#if !defined(JSON_USE_INT64_DOUBLE_CONVERSION) + return static_cast<float>( value_.uint_ ); +#else // if !defined(JSON_USE_INT64_DOUBLE_CONVERSION) + return integerToDouble( value_.uint_ ); +#endif // if !defined(JSON_USE_INT64_DOUBLE_CONVERSION) + case realValue: + return static_cast<float>( value_.real_ ); + case nullValue: + return 0.0; + case booleanValue: + return value_.bool_ ? 1.0f : 0.0f; + default: + break; + } + JSON_FAIL_MESSAGE("Value is not convertible to float."); +} + +bool +Value::asBool() const +{ + switch ( type_ ) + { + case booleanValue: + return value_.bool_; + case nullValue: + return false; + case intValue: + return value_.int_ ? true : false; + case uintValue: + return value_.uint_ ? true : false; + case realValue: + return value_.real_ ? true : false; + default: + break; + } + JSON_FAIL_MESSAGE("Value is not convertible to bool."); +} + + +bool +Value::isConvertibleTo( ValueType other ) const +{ + switch ( other ) + { + case nullValue: + return ( isNumeric() && asDouble() == 0.0 ) + || ( type_ == booleanValue && value_.bool_ == false ) + || ( type_ == stringValue && asString() == "" ) + || ( type_ == arrayValue && value_.map_->size() == 0 ) + || ( type_ == objectValue && value_.map_->size() == 0 ) + || type_ == nullValue; + case intValue: + return isInt() + || (type_ == realValue && InRange(value_.real_, minInt, maxInt)) + || type_ == booleanValue + || type_ == nullValue; + case uintValue: + return isUInt() + || (type_ == realValue && InRange(value_.real_, 0, maxUInt)) + || type_ == booleanValue + || type_ == nullValue; + case realValue: + return isNumeric() + || type_ == booleanValue + || type_ == nullValue; + case booleanValue: + return isNumeric() + || type_ == booleanValue + || type_ == nullValue; + case stringValue: + return isNumeric() + || type_ == booleanValue + || type_ == stringValue + || type_ == nullValue; + case arrayValue: + return type_ == arrayValue + || type_ == nullValue; + case objectValue: + return type_ == objectValue + || type_ == nullValue; + } + JSON_ASSERT_UNREACHABLE; + return false; +} + + +/// Number of values in array or object +ArrayIndex +Value::size() const +{ + switch ( type_ ) + { + case nullValue: + case intValue: + case uintValue: + case realValue: + case booleanValue: + case stringValue: + return 0; +#ifndef JSON_VALUE_USE_INTERNAL_MAP + case arrayValue: // size of the array is highest index + 1 + if ( !value_.map_->empty() ) + { + ObjectValues::const_iterator itLast = value_.map_->end(); + --itLast; + return (*itLast).first.index()+1; + } + return 0; + case objectValue: + return ArrayIndex( value_.map_->size() ); +#else + case arrayValue: + return Int( value_.array_->size() ); + case objectValue: + return Int( value_.map_->size() ); +#endif + } + JSON_ASSERT_UNREACHABLE; + return 0; // unreachable; +} + + +bool +Value::empty() const +{ + if ( isNull() || isArray() || isObject() ) + return size() == 0u; + else + return false; +} + + +bool +Value::operator!() const +{ + return isNull(); +} + + +void +Value::clear() +{ + JSON_ASSERT( type_ == nullValue || type_ == arrayValue || type_ == objectValue ); + + switch ( type_ ) + { +#ifndef JSON_VALUE_USE_INTERNAL_MAP + case arrayValue: + case objectValue: + value_.map_->clear(); + break; +#else + case arrayValue: + value_.array_->clear(); + break; + case objectValue: + value_.map_->clear(); + break; +#endif + default: + break; + } +} + +void +Value::resize( ArrayIndex newSize ) +{ + JSON_ASSERT( type_ == nullValue || type_ == arrayValue ); + if ( type_ == nullValue ) + *this = Value( arrayValue ); +#ifndef JSON_VALUE_USE_INTERNAL_MAP + ArrayIndex oldSize = size(); + if ( newSize == 0 ) + clear(); + else if ( newSize > oldSize ) + (*this)[ newSize - 1 ]; + else + { + for ( ArrayIndex index = newSize; index < oldSize; ++index ) + { + value_.map_->erase( index ); + } + assert( size() == newSize ); + } +#else + value_.array_->resize( newSize ); +#endif +} + + +Value & +Value::operator[]( ArrayIndex index ) +{ + JSON_ASSERT( type_ == nullValue || type_ == arrayValue ); + if ( type_ == nullValue ) + *this = Value( arrayValue ); +#ifndef JSON_VALUE_USE_INTERNAL_MAP + CZString key( index ); + ObjectValues::iterator it = value_.map_->lower_bound( key ); + if ( it != value_.map_->end() && (*it).first == key ) + return (*it).second; + + ObjectValues::value_type defaultValue( key, null ); + it = value_.map_->insert( it, defaultValue ); + return (*it).second; +#else + return value_.array_->resolveReference( index ); +#endif +} + + +Value & +Value::operator[]( int index ) +{ + JSON_ASSERT( index >= 0 ); + return (*this)[ ArrayIndex(index) ]; +} + + +const Value & +Value::operator[]( ArrayIndex index ) const +{ + JSON_ASSERT( type_ == nullValue || type_ == arrayValue ); + if ( type_ == nullValue ) + return null; +#ifndef JSON_VALUE_USE_INTERNAL_MAP + CZString key( index ); + ObjectValues::const_iterator it = value_.map_->find( key ); + if ( it == value_.map_->end() ) + return null; + return (*it).second; +#else + Value *value = value_.array_->find( index ); + return value ? *value : null; +#endif +} + + +const Value & +Value::operator[]( int index ) const +{ + JSON_ASSERT( index >= 0 ); + return (*this)[ ArrayIndex(index) ]; +} + + +Value & +Value::operator[]( const char *key ) +{ + return resolveReference( key, false ); +} + + +Value & +Value::resolveReference( const char *key, + bool isStatic ) +{ + JSON_ASSERT( type_ == nullValue || type_ == objectValue ); + if ( type_ == nullValue ) + *this = Value( objectValue ); +#ifndef JSON_VALUE_USE_INTERNAL_MAP + CZString actualKey( key, isStatic ? CZString::noDuplication + : CZString::duplicateOnCopy ); + ObjectValues::iterator it = value_.map_->lower_bound( actualKey ); + if ( it != value_.map_->end() && (*it).first == actualKey ) + return (*it).second; + + ObjectValues::value_type defaultValue( actualKey, null ); + it = value_.map_->insert( it, defaultValue ); + Value &value = (*it).second; + return value; +#else + return value_.map_->resolveReference( key, isStatic ); +#endif +} + + +Value +Value::get( ArrayIndex index, + const Value &defaultValue ) const +{ + const Value *value = &((*this)[index]); + return value == &null ? defaultValue : *value; +} + + +bool +Value::isValidIndex( ArrayIndex index ) const +{ + return index < size(); +} + + + +const Value & +Value::operator[]( const char *key ) const +{ + JSON_ASSERT( type_ == nullValue || type_ == objectValue ); + if ( type_ == nullValue ) + return null; +#ifndef JSON_VALUE_USE_INTERNAL_MAP + CZString actualKey( key, CZString::noDuplication ); + ObjectValues::const_iterator it = value_.map_->find( actualKey ); + if ( it == value_.map_->end() ) + return null; + return (*it).second; +#else + const Value *value = value_.map_->find( key ); + return value ? *value : null; +#endif +} + + +Value & +Value::operator[]( const std::string &key ) +{ + return (*this)[ key.c_str() ]; +} + + +const Value & +Value::operator[]( const std::string &key ) const +{ + return (*this)[ key.c_str() ]; +} + +Value & +Value::operator[]( const StaticString &key ) +{ + return resolveReference( key, true ); +} + + +# ifdef JSON_USE_CPPTL +Value & +Value::operator[]( const CppTL::ConstString &key ) +{ + return (*this)[ key.c_str() ]; +} + + +const Value & +Value::operator[]( const CppTL::ConstString &key ) const +{ + return (*this)[ key.c_str() ]; +} +# endif + + +Value & +Value::append( const Value &value ) +{ + return (*this)[size()] = value; +} + + +Value +Value::get( const char *key, + const Value &defaultValue ) const +{ + const Value *value = &((*this)[key]); + return value == &null ? defaultValue : *value; +} + + +Value +Value::get( const std::string &key, + const Value &defaultValue ) const +{ + return get( key.c_str(), defaultValue ); +} + +Value +Value::removeMember( const char* key ) +{ + JSON_ASSERT( type_ == nullValue || type_ == objectValue ); + if ( type_ == nullValue ) + return null; +#ifndef JSON_VALUE_USE_INTERNAL_MAP + CZString actualKey( key, CZString::noDuplication ); + ObjectValues::iterator it = value_.map_->find( actualKey ); + if ( it == value_.map_->end() ) + return null; + Value old(it->second); + value_.map_->erase(it); + return old; +#else + Value *value = value_.map_->find( key ); + if (value){ + Value old(*value); + value_.map_.remove( key ); + return old; + } else { + return null; + } +#endif +} + +Value +Value::removeMember( const std::string &key ) +{ + return removeMember( key.c_str() ); +} + +# ifdef JSON_USE_CPPTL +Value +Value::get( const CppTL::ConstString &key, + const Value &defaultValue ) const +{ + return get( key.c_str(), defaultValue ); +} +# endif + +bool +Value::isMember( const char *key ) const +{ + const Value *value = &((*this)[key]); + return value != &null; +} + + +bool +Value::isMember( const std::string &key ) const +{ + return isMember( key.c_str() ); +} + + +# ifdef JSON_USE_CPPTL +bool +Value::isMember( const CppTL::ConstString &key ) const +{ + return isMember( key.c_str() ); +} +#endif + +Value::Members +Value::getMemberNames() const +{ + JSON_ASSERT( type_ == nullValue || type_ == objectValue ); + if ( type_ == nullValue ) + return Value::Members(); + Members members; + members.reserve( value_.map_->size() ); +#ifndef JSON_VALUE_USE_INTERNAL_MAP + ObjectValues::const_iterator it = value_.map_->begin(); + ObjectValues::const_iterator itEnd = value_.map_->end(); + for ( ; it != itEnd; ++it ) + members.push_back( std::string( (*it).first.c_str() ) ); +#else + ValueInternalMap::IteratorState it; + ValueInternalMap::IteratorState itEnd; + value_.map_->makeBeginIterator( it ); + value_.map_->makeEndIterator( itEnd ); + for ( ; !ValueInternalMap::equals( it, itEnd ); ValueInternalMap::increment(it) ) + members.push_back( std::string( ValueInternalMap::key( it ) ) ); +#endif + return members; +} +// +//# ifdef JSON_USE_CPPTL +//EnumMemberNames +//Value::enumMemberNames() const +//{ +// if ( type_ == objectValue ) +// { +// return CppTL::Enum::any( CppTL::Enum::transform( +// CppTL::Enum::keys( *(value_.map_), CppTL::Type<const CZString &>() ), +// MemberNamesTransform() ) ); +// } +// return EnumMemberNames(); +//} +// +// +//EnumValues +//Value::enumValues() const +//{ +// if ( type_ == objectValue || type_ == arrayValue ) +// return CppTL::Enum::anyValues( *(value_.map_), +// CppTL::Type<const Value &>() ); +// return EnumValues(); +//} +// +//# endif + +static bool IsIntegral(double d) { + double integral_part; + return modf(d, &integral_part) == 0.0; +} + + +bool +Value::isNull() const +{ + return type_ == nullValue; +} + + +bool +Value::isBool() const +{ + return type_ == booleanValue; +} + + +bool +Value::isInt() const +{ + switch ( type_ ) + { + case intValue: + return value_.int_ >= minInt && value_.int_ <= maxInt; + case uintValue: + return value_.uint_ <= UInt(maxInt); + case realValue: + return value_.real_ >= minInt && + value_.real_ <= maxInt && + IsIntegral(value_.real_); + default: + break; + } + return false; +} + + +bool +Value::isUInt() const +{ + switch ( type_ ) + { + case intValue: + return value_.int_ >= 0 && LargestUInt(value_.int_) <= LargestUInt(maxUInt); + case uintValue: + return value_.uint_ <= maxUInt; + case realValue: + return value_.real_ >= 0 && + value_.real_ <= maxUInt && + IsIntegral(value_.real_); + default: + break; + } + return false; +} + +bool +Value::isInt64() const +{ +# if defined(JSON_HAS_INT64) + switch ( type_ ) + { + case intValue: + return true; + case uintValue: + return value_.uint_ <= UInt64(maxInt64); + case realValue: + // Note that maxInt64 (= 2^63 - 1) is not exactly representable as a + // double, so double(maxInt64) will be rounded up to 2^63. Therefore we + // require the value to be strictly less than the limit. + return value_.real_ >= double(minInt64) && + value_.real_ < double(maxInt64) && + IsIntegral(value_.real_); + default: + break; + } +# endif // JSON_HAS_INT64 + return false; +} + +bool +Value::isUInt64() const +{ +# if defined(JSON_HAS_INT64) + switch ( type_ ) + { + case intValue: + return value_.int_ >= 0; + case uintValue: + return true; + case realValue: + // Note that maxUInt64 (= 2^64 - 1) is not exactly representable as a + // double, so double(maxUInt64) will be rounded up to 2^64. Therefore we + // require the value to be strictly less than the limit. + return value_.real_ >= 0 && + value_.real_ < maxUInt64AsDouble && + IsIntegral(value_.real_); + default: + break; + } +# endif // JSON_HAS_INT64 + return false; +} + + +bool +Value::isIntegral() const +{ +#if defined(JSON_HAS_INT64) + return isInt64() || isUInt64(); +#else + return isInt() || isUInt(); +#endif +} + + +bool +Value::isDouble() const +{ + return type_ == realValue || isIntegral(); +} + + +bool +Value::isNumeric() const +{ + return isIntegral() || isDouble(); +} + + +bool +Value::isString() const +{ + return type_ == stringValue; +} + + +bool +Value::isArray() const +{ + return type_ == arrayValue; +} + + +bool +Value::isObject() const +{ + return type_ == objectValue; +} + + +void +Value::setComment( const char *comment, + CommentPlacement placement ) +{ + if ( !comments_ ) + comments_ = new CommentInfo[numberOfCommentPlacement]; + comments_[placement].setComment( comment ); +} + + +void +Value::setComment( const std::string &comment, + CommentPlacement placement ) +{ + setComment( comment.c_str(), placement ); +} + + +bool +Value::hasComment( CommentPlacement placement ) const +{ + return comments_ != 0 && comments_[placement].comment_ != 0; +} + +std::string +Value::getComment( CommentPlacement placement ) const +{ + if ( hasComment(placement) ) + return comments_[placement].comment_; + return ""; +} + + +std::string +Value::toStyledString() const +{ + StyledWriter writer; + return writer.write( *this ); +} + + +Value::const_iterator +Value::begin() const +{ + switch ( type_ ) + { +#ifdef JSON_VALUE_USE_INTERNAL_MAP + case arrayValue: + if ( value_.array_ ) + { + ValueInternalArray::IteratorState it; + value_.array_->makeBeginIterator( it ); + return const_iterator( it ); + } + break; + case objectValue: + if ( value_.map_ ) + { + ValueInternalMap::IteratorState it; + value_.map_->makeBeginIterator( it ); + return const_iterator( it ); + } + break; +#else + case arrayValue: + case objectValue: + if ( value_.map_ ) + return const_iterator( value_.map_->begin() ); + break; +#endif + default: + break; + } + return const_iterator(); +} + +Value::const_iterator +Value::end() const +{ + switch ( type_ ) + { +#ifdef JSON_VALUE_USE_INTERNAL_MAP + case arrayValue: + if ( value_.array_ ) + { + ValueInternalArray::IteratorState it; + value_.array_->makeEndIterator( it ); + return const_iterator( it ); + } + break; + case objectValue: + if ( value_.map_ ) + { + ValueInternalMap::IteratorState it; + value_.map_->makeEndIterator( it ); + return const_iterator( it ); + } + break; +#else + case arrayValue: + case objectValue: + if ( value_.map_ ) + return const_iterator( value_.map_->end() ); + break; +#endif + default: + break; + } + return const_iterator(); +} + + +Value::iterator +Value::begin() +{ + switch ( type_ ) + { +#ifdef JSON_VALUE_USE_INTERNAL_MAP + case arrayValue: + if ( value_.array_ ) + { + ValueInternalArray::IteratorState it; + value_.array_->makeBeginIterator( it ); + return iterator( it ); + } + break; + case objectValue: + if ( value_.map_ ) + { + ValueInternalMap::IteratorState it; + value_.map_->makeBeginIterator( it ); + return iterator( it ); + } + break; +#else + case arrayValue: + case objectValue: + if ( value_.map_ ) + return iterator( value_.map_->begin() ); + break; +#endif + default: + break; + } + return iterator(); +} + +Value::iterator +Value::end() +{ + switch ( type_ ) + { +#ifdef JSON_VALUE_USE_INTERNAL_MAP + case arrayValue: + if ( value_.array_ ) + { + ValueInternalArray::IteratorState it; + value_.array_->makeEndIterator( it ); + return iterator( it ); + } + break; + case objectValue: + if ( value_.map_ ) + { + ValueInternalMap::IteratorState it; + value_.map_->makeEndIterator( it ); + return iterator( it ); + } + break; +#else + case arrayValue: + case objectValue: + if ( value_.map_ ) + return iterator( value_.map_->end() ); + break; +#endif + default: + break; + } + return iterator(); +} + + +// class PathArgument +// ////////////////////////////////////////////////////////////////// + +PathArgument::PathArgument() + : key_() + , index_() + , kind_( kindNone ) +{ +} + + +PathArgument::PathArgument( ArrayIndex index ) + : key_() + , index_( index ) + , kind_( kindIndex ) +{ +} + + +PathArgument::PathArgument( const char *key ) + : key_( key ) + , index_() + , kind_( kindKey ) +{ +} + + +PathArgument::PathArgument( const std::string &key ) + : key_( key.c_str() ) + , index_() + , kind_( kindKey ) +{ +} + +// class Path +// ////////////////////////////////////////////////////////////////// + +Path::Path( const std::string &path, + const PathArgument &a1, + const PathArgument &a2, + const PathArgument &a3, + const PathArgument &a4, + const PathArgument &a5 ) +{ + InArgs in; + in.push_back( &a1 ); + in.push_back( &a2 ); + in.push_back( &a3 ); + in.push_back( &a4 ); + in.push_back( &a5 ); + makePath( path, in ); +} + + +void +Path::makePath( const std::string &path, + const InArgs &in ) +{ + const char *current = path.c_str(); + const char *end = current + path.length(); + InArgs::const_iterator itInArg = in.begin(); + while ( current != end ) + { + if ( *current == '[' ) + { + ++current; + if ( *current == '%' ) + addPathInArg( path, in, itInArg, PathArgument::kindIndex ); + else + { + ArrayIndex index = 0; + for ( ; current != end && *current >= '0' && *current <= '9'; ++current ) + index = index * 10 + ArrayIndex(*current - '0'); + args_.push_back( index ); + } + if ( current == end || *current++ != ']' ) + invalidPath( path, int(current - path.c_str()) ); + } + else if ( *current == '%' ) + { + addPathInArg( path, in, itInArg, PathArgument::kindKey ); + ++current; + } + else if ( *current == '.' ) + { + ++current; + } + else + { + const char *beginName = current; + while ( current != end && !strchr( "[.", *current ) ) + ++current; + args_.push_back( std::string( beginName, current ) ); + } + } +} + + +void +Path::addPathInArg( const std::string &path, + const InArgs &in, + InArgs::const_iterator &itInArg, + PathArgument::Kind kind ) +{ + if ( itInArg == in.end() ) + { + // Error: missing argument %d + } + else if ( (*itInArg)->kind_ != kind ) + { + // Error: bad argument type + } + else + { + args_.push_back( **itInArg ); + } +} + + +void +Path::invalidPath( const std::string &path, + int location ) +{ + // Error: invalid path. +} + + +const Value & +Path::resolve( const Value &root ) const +{ + const Value *node = &root; + for ( Args::const_iterator it = args_.begin(); it != args_.end(); ++it ) + { + const PathArgument &arg = *it; + if ( arg.kind_ == PathArgument::kindIndex ) + { + if ( !node->isArray() || !node->isValidIndex( arg.index_ ) ) + { + // Error: unable to resolve path (array value expected at position... + } + node = &((*node)[arg.index_]); + } + else if ( arg.kind_ == PathArgument::kindKey ) + { + if ( !node->isObject() ) + { + // Error: unable to resolve path (object value expected at position...) + } + node = &((*node)[arg.key_]); + if ( node == &Value::null ) + { + // Error: unable to resolve path (object has no member named '' at position...) + } + } + } + return *node; +} + + +Value +Path::resolve( const Value &root, + const Value &defaultValue ) const +{ + const Value *node = &root; + for ( Args::const_iterator it = args_.begin(); it != args_.end(); ++it ) + { + const PathArgument &arg = *it; + if ( arg.kind_ == PathArgument::kindIndex ) + { + if ( !node->isArray() || !node->isValidIndex( arg.index_ ) ) + return defaultValue; + node = &((*node)[arg.index_]); + } + else if ( arg.kind_ == PathArgument::kindKey ) + { + if ( !node->isObject() ) + return defaultValue; + node = &((*node)[arg.key_]); + if ( node == &Value::null ) + return defaultValue; + } + } + return *node; +} + + +Value & +Path::make( Value &root ) const +{ + Value *node = &root; + for ( Args::const_iterator it = args_.begin(); it != args_.end(); ++it ) + { + const PathArgument &arg = *it; + if ( arg.kind_ == PathArgument::kindIndex ) + { + if ( !node->isArray() ) + { + // Error: node is not an array at position ... + } + node = &((*node)[arg.index_]); + } + else if ( arg.kind_ == PathArgument::kindKey ) + { + if ( !node->isObject() ) + { + // Error: node is not an object at position... + } + node = &((*node)[arg.key_]); + } + } + return *node; +} + + +} // namespace Json
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/third_party/json/json_valueiterator.inl Thu Aug 25 11:05:55 2011 +0100 @@ -0,0 +1,299 @@ +// Copyright 2007-2010 Baptiste Lepilleur +// Distributed under MIT license, or public domain if desired and +// recognized in your jurisdiction. +// See file LICENSE for detail or copy at http://jsoncpp.sourceforge.net/LICENSE + +// included by json_value.cpp + +namespace Json { + +// ////////////////////////////////////////////////////////////////// +// ////////////////////////////////////////////////////////////////// +// ////////////////////////////////////////////////////////////////// +// class ValueIteratorBase +// ////////////////////////////////////////////////////////////////// +// ////////////////////////////////////////////////////////////////// +// ////////////////////////////////////////////////////////////////// + +ValueIteratorBase::ValueIteratorBase() +#ifndef JSON_VALUE_USE_INTERNAL_MAP + : current_() + , isNull_( true ) +{ +} +#else + : isArray_( true ) + , isNull_( true ) +{ + iterator_.array_ = ValueInternalArray::IteratorState(); +} +#endif + + +#ifndef JSON_VALUE_USE_INTERNAL_MAP +ValueIteratorBase::ValueIteratorBase( const Value::ObjectValues::iterator ¤t ) + : current_( current ) + , isNull_( false ) +{ +} +#else +ValueIteratorBase::ValueIteratorBase( const ValueInternalArray::IteratorState &state ) + : isArray_( true ) +{ + iterator_.array_ = state; +} + + +ValueIteratorBase::ValueIteratorBase( const ValueInternalMap::IteratorState &state ) + : isArray_( false ) +{ + iterator_.map_ = state; +} +#endif + +Value & +ValueIteratorBase::deref() const +{ +#ifndef JSON_VALUE_USE_INTERNAL_MAP + return current_->second; +#else + if ( isArray_ ) + return ValueInternalArray::dereference( iterator_.array_ ); + return ValueInternalMap::value( iterator_.map_ ); +#endif +} + + +void +ValueIteratorBase::increment() +{ +#ifndef JSON_VALUE_USE_INTERNAL_MAP + ++current_; +#else + if ( isArray_ ) + ValueInternalArray::increment( iterator_.array_ ); + ValueInternalMap::increment( iterator_.map_ ); +#endif +} + + +void +ValueIteratorBase::decrement() +{ +#ifndef JSON_VALUE_USE_INTERNAL_MAP + --current_; +#else + if ( isArray_ ) + ValueInternalArray::decrement( iterator_.array_ ); + ValueInternalMap::decrement( iterator_.map_ ); +#endif +} + + +ValueIteratorBase::difference_type +ValueIteratorBase::computeDistance( const SelfType &other ) const +{ +#ifndef JSON_VALUE_USE_INTERNAL_MAP +# ifdef JSON_USE_CPPTL_SMALLMAP + return current_ - other.current_; +# else + // Iterator for null value are initialized using the default + // constructor, which initialize current_ to the default + // std::map::iterator. As begin() and end() are two instance + // of the default std::map::iterator, they can not be compared. + // To allow this, we handle this comparison specifically. + if ( isNull_ && other.isNull_ ) + { + return 0; + } + + + // Usage of std::distance is not portable (does not compile with Sun Studio 12 RogueWave STL, + // which is the one used by default). + // Using a portable hand-made version for non random iterator instead: + // return difference_type( std::distance( current_, other.current_ ) ); + difference_type myDistance = 0; + for ( Value::ObjectValues::iterator it = current_; it != other.current_; ++it ) + { + ++myDistance; + } + return myDistance; +# endif +#else + if ( isArray_ ) + return ValueInternalArray::distance( iterator_.array_, other.iterator_.array_ ); + return ValueInternalMap::distance( iterator_.map_, other.iterator_.map_ ); +#endif +} + + +bool +ValueIteratorBase::isEqual( const SelfType &other ) const +{ +#ifndef JSON_VALUE_USE_INTERNAL_MAP + if ( isNull_ ) + { + return other.isNull_; + } + return current_ == other.current_; +#else + if ( isArray_ ) + return ValueInternalArray::equals( iterator_.array_, other.iterator_.array_ ); + return ValueInternalMap::equals( iterator_.map_, other.iterator_.map_ ); +#endif +} + + +void +ValueIteratorBase::copy( const SelfType &other ) +{ +#ifndef JSON_VALUE_USE_INTERNAL_MAP + current_ = other.current_; +#else + if ( isArray_ ) + iterator_.array_ = other.iterator_.array_; + iterator_.map_ = other.iterator_.map_; +#endif +} + + +Value +ValueIteratorBase::key() const +{ +#ifndef JSON_VALUE_USE_INTERNAL_MAP + const Value::CZString czstring = (*current_).first; + if ( czstring.c_str() ) + { + if ( czstring.isStaticString() ) + return Value( StaticString( czstring.c_str() ) ); + return Value( czstring.c_str() ); + } + return Value( czstring.index() ); +#else + if ( isArray_ ) + return Value( ValueInternalArray::indexOf( iterator_.array_ ) ); + bool isStatic; + const char *memberName = ValueInternalMap::key( iterator_.map_, isStatic ); + if ( isStatic ) + return Value( StaticString( memberName ) ); + return Value( memberName ); +#endif +} + + +UInt +ValueIteratorBase::index() const +{ +#ifndef JSON_VALUE_USE_INTERNAL_MAP + const Value::CZString czstring = (*current_).first; + if ( !czstring.c_str() ) + return czstring.index(); + return Value::UInt( -1 ); +#else + if ( isArray_ ) + return Value::UInt( ValueInternalArray::indexOf( iterator_.array_ ) ); + return Value::UInt( -1 ); +#endif +} + + +const char * +ValueIteratorBase::memberName() const +{ +#ifndef JSON_VALUE_USE_INTERNAL_MAP + const char *name = (*current_).first.c_str(); + return name ? name : ""; +#else + if ( !isArray_ ) + return ValueInternalMap::key( iterator_.map_ ); + return ""; +#endif +} + + +// ////////////////////////////////////////////////////////////////// +// ////////////////////////////////////////////////////////////////// +// ////////////////////////////////////////////////////////////////// +// class ValueConstIterator +// ////////////////////////////////////////////////////////////////// +// ////////////////////////////////////////////////////////////////// +// ////////////////////////////////////////////////////////////////// + +ValueConstIterator::ValueConstIterator() +{ +} + + +#ifndef JSON_VALUE_USE_INTERNAL_MAP +ValueConstIterator::ValueConstIterator( const Value::ObjectValues::iterator ¤t ) + : ValueIteratorBase( current ) +{ +} +#else +ValueConstIterator::ValueConstIterator( const ValueInternalArray::IteratorState &state ) + : ValueIteratorBase( state ) +{ +} + +ValueConstIterator::ValueConstIterator( const ValueInternalMap::IteratorState &state ) + : ValueIteratorBase( state ) +{ +} +#endif + +ValueConstIterator & +ValueConstIterator::operator =( const ValueIteratorBase &other ) +{ + copy( other ); + return *this; +} + + +// ////////////////////////////////////////////////////////////////// +// ////////////////////////////////////////////////////////////////// +// ////////////////////////////////////////////////////////////////// +// class ValueIterator +// ////////////////////////////////////////////////////////////////// +// ////////////////////////////////////////////////////////////////// +// ////////////////////////////////////////////////////////////////// + +ValueIterator::ValueIterator() +{ +} + + +#ifndef JSON_VALUE_USE_INTERNAL_MAP +ValueIterator::ValueIterator( const Value::ObjectValues::iterator ¤t ) + : ValueIteratorBase( current ) +{ +} +#else +ValueIterator::ValueIterator( const ValueInternalArray::IteratorState &state ) + : ValueIteratorBase( state ) +{ +} + +ValueIterator::ValueIterator( const ValueInternalMap::IteratorState &state ) + : ValueIteratorBase( state ) +{ +} +#endif + +ValueIterator::ValueIterator( const ValueConstIterator &other ) + : ValueIteratorBase( other ) +{ +} + +ValueIterator::ValueIterator( const ValueIterator &other ) + : ValueIteratorBase( other ) +{ +} + +ValueIterator & +ValueIterator::operator =( const SelfType &other ) +{ + copy( other ); + return *this; +} + +} // namespace Json
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/third_party/json/json_writer.cpp Thu Aug 25 11:05:55 2011 +0100 @@ -0,0 +1,842 @@ +// Copyright 2011 Baptiste Lepilleur +// Distributed under MIT license, or public domain if desired and +// recognized in your jurisdiction. +// See file LICENSE for detail or copy at http://jsoncpp.sourceforge.net/LICENSE + +#if !defined(JSON_IS_AMALGAMATION) +# include <json/writer.h> +# include "json_tool.h" +#endif // if !defined(JSON_IS_AMALGAMATION) +#include <utility> +#include <assert.h> +#include <stdio.h> +#include <string.h> +#include <iostream> +#include <sstream> +#include <iomanip> + +#if _MSC_VER >= 1400 // VC++ 8.0 +#pragma warning( disable : 4996 ) // disable warning about strdup being deprecated. +#endif + +namespace Json { + +static bool containsControlCharacter( const char* str ) +{ + while ( *str ) + { + if ( isControlCharacter( *(str++) ) ) + return true; + } + return false; +} + + +std::string valueToString( LargestInt value ) +{ + UIntToStringBuffer buffer; + char *current = buffer + sizeof(buffer); + bool isNegative = value < 0; + if ( isNegative ) + value = -value; + uintToString( LargestUInt(value), current ); + if ( isNegative ) + *--current = '-'; + assert( current >= buffer ); + return current; +} + + +std::string valueToString( LargestUInt value ) +{ + UIntToStringBuffer buffer; + char *current = buffer + sizeof(buffer); + uintToString( value, current ); + assert( current >= buffer ); + return current; +} + +#if defined(JSON_HAS_INT64) + +std::string valueToString( Int value ) +{ + return valueToString( LargestInt(value) ); +} + + +std::string valueToString( UInt value ) +{ + return valueToString( LargestUInt(value) ); +} + +#endif // # if defined(JSON_HAS_INT64) + + +std::string valueToString( double value ) +{ + char buffer[32]; +#if defined(_MSC_VER) && defined(__STDC_SECURE_LIB__) // Use secure version with visual studio 2005 to avoid warning. + sprintf_s(buffer, sizeof(buffer), "%#.16g", value); +#else + sprintf(buffer, "%#.16g", value); +#endif + char* ch = buffer + strlen(buffer) - 1; + if (*ch != '0') return buffer; // nothing to truncate, so save time + while(ch > buffer && *ch == '0'){ + --ch; + } + char* last_nonzero = ch; + while(ch >= buffer){ + switch(*ch){ + case '0': + case '1': + case '2': + case '3': + case '4': + case '5': + case '6': + case '7': + case '8': + case '9': + --ch; + continue; + case '.': + // Truncate zeroes to save bytes in output, but keep one. + *(last_nonzero+2) = '\0'; + return buffer; + default: + return buffer; + } + } + return buffer; +} + + +std::string valueToString( bool value ) +{ + return value ? "true" : "false"; +} + +std::string valueToQuotedString( const char *value ) +{ + if (value == NULL) + return ""; + // Not sure how to handle unicode... + if (strpbrk(value, "\"\\\b\f\n\r\t") == NULL && !containsControlCharacter( value )) + return std::string("\"") + value + "\""; + // We have to walk value and escape any special characters. + // Appending to std::string is not efficient, but this should be rare. + // (Note: forward slashes are *not* rare, but I am not escaping them.) + std::string::size_type maxsize = strlen(value)*2 + 3; // allescaped+quotes+NULL + std::string result; + result.reserve(maxsize); // to avoid lots of mallocs + result += "\""; + for (const char* c=value; *c != 0; ++c) + { + switch(*c) + { + case '\"': + result += "\\\""; + break; + case '\\': + result += "\\\\"; + break; + case '\b': + result += "\\b"; + break; + case '\f': + result += "\\f"; + break; + case '\n': + result += "\\n"; + break; + case '\r': + result += "\\r"; + break; + case '\t': + result += "\\t"; + break; + //case '/': + // Even though \/ is considered a legal escape in JSON, a bare + // slash is also legal, so I see no reason to escape it. + // (I hope I am not misunderstanding something. + // blep notes: actually escaping \/ may be useful in javascript to avoid </ + // sequence. + // Should add a flag to allow this compatibility mode and prevent this + // sequence from occurring. + default: + if ( isControlCharacter( *c ) ) + { + std::ostringstream oss; + oss << "\\u" << std::hex << std::uppercase << std::setfill('0') << std::setw(4) << static_cast<int>(*c); + result += oss.str(); + } + else + { + result += *c; + } + break; + } + } + result += "\""; + return result; +} + +// Class Writer +// ////////////////////////////////////////////////////////////////// +Writer::~Writer() +{ +} + + +// Class FastWriter +// ////////////////////////////////////////////////////////////////// + +FastWriter::FastWriter() + : yamlCompatiblityEnabled_( false ) +{ +} + + +void +FastWriter::enableYAMLCompatibility() +{ + yamlCompatiblityEnabled_ = true; +} + + +std::string +FastWriter::write( const Value &root ) +{ + document_ = ""; + writeValue( root ); + document_ += "\n"; + return document_; +} + + +void +FastWriter::writeValue( const Value &value ) +{ + switch ( value.type() ) + { + case nullValue: + document_ += "null"; + break; + case intValue: + document_ += valueToString( value.asLargestInt() ); + break; + case uintValue: + document_ += valueToString( value.asLargestUInt() ); + break; + case realValue: + document_ += valueToString( value.asDouble() ); + break; + case stringValue: + document_ += valueToQuotedString( value.asCString() ); + break; + case booleanValue: + document_ += valueToString( value.asBool() ); + break; + case arrayValue: + { + document_ += "["; + int size = value.size(); + for ( int index =0; index < size; ++index ) + { + if ( index > 0 ) + document_ += ","; + writeValue( value[index] ); + } + document_ += "]"; + } + break; + case objectValue: + { + Value::Members members( value.getMemberNames() ); + document_ += "{"; + for ( Value::Members::iterator it = members.begin(); + it != members.end(); + ++it ) + { + const std::string &name = *it; + if ( it != members.begin() ) + document_ += ","; + document_ += valueToQuotedString( name.c_str() ); + document_ += yamlCompatiblityEnabled_ ? ": " + : ":"; + writeValue( value[name] ); + } + document_ += "}"; + } + break; + } +} + + +// Class StyledWriter +// ////////////////////////////////////////////////////////////////// + +StyledWriter::StyledWriter() + : rightMargin_( 74 ) + , indentSize_( 3 ) + , addChildValues_() +{ +} + + +std::string +StyledWriter::write( const Value &root ) +{ + document_ = ""; + addChildValues_ = false; + indentString_ = ""; + writeCommentBeforeValue( root ); + writeValue( root ); + writeCommentAfterValueOnSameLine( root ); + document_ += "\n"; + return document_; +} + + +void +StyledWriter::writeValue( const Value &value ) +{ + switch ( value.type() ) + { + case nullValue: + pushValue( "null" ); + break; + case intValue: + pushValue( valueToString( value.asLargestInt() ) ); + break; + case uintValue: + pushValue( valueToString( value.asLargestUInt() ) ); + break; + case realValue: + pushValue( valueToString( value.asDouble() ) ); + break; + case stringValue: + pushValue( valueToQuotedString( value.asCString() ) ); + break; + case booleanValue: + pushValue( valueToString( value.asBool() ) ); + break; + case arrayValue: + writeArrayValue( value); + break; + case objectValue: + { + Value::Members members( value.getMemberNames() ); + if ( members.empty() ) + pushValue( "{}" ); + else + { + writeWithIndent( "{" ); + indent(); + Value::Members::iterator it = members.begin(); + for (;;) + { + const std::string &name = *it; + const Value &childValue = value[name]; + writeCommentBeforeValue( childValue ); + writeWithIndent( valueToQuotedString( name.c_str() ) ); + document_ += " : "; + writeValue( childValue ); + if ( ++it == members.end() ) + { + writeCommentAfterValueOnSameLine( childValue ); + break; + } + document_ += ","; + writeCommentAfterValueOnSameLine( childValue ); + } + unindent(); + writeWithIndent( "}" ); + } + } + break; + } +} + + +void +StyledWriter::writeArrayValue( const Value &value ) +{ + unsigned size = value.size(); + if ( size == 0 ) + pushValue( "[]" ); + else + { + bool isArrayMultiLine = isMultineArray( value ); + if ( isArrayMultiLine ) + { + writeWithIndent( "[" ); + indent(); + bool hasChildValue = !childValues_.empty(); + unsigned index =0; + for (;;) + { + const Value &childValue = value[index]; + writeCommentBeforeValue( childValue ); + if ( hasChildValue ) + writeWithIndent( childValues_[index] ); + else + { + writeIndent(); + writeValue( childValue ); + } + if ( ++index == size ) + { + writeCommentAfterValueOnSameLine( childValue ); + break; + } + document_ += ","; + writeCommentAfterValueOnSameLine( childValue ); + } + unindent(); + writeWithIndent( "]" ); + } + else // output on a single line + { + assert( childValues_.size() == size ); + document_ += "[ "; + for ( unsigned index =0; index < size; ++index ) + { + if ( index > 0 ) + document_ += ", "; + document_ += childValues_[index]; + } + document_ += " ]"; + } + } +} + + +bool +StyledWriter::isMultineArray( const Value &value ) +{ + int size = value.size(); + bool isMultiLine = size*3 >= rightMargin_ ; + childValues_.clear(); + for ( int index =0; index < size && !isMultiLine; ++index ) + { + const Value &childValue = value[index]; + isMultiLine = isMultiLine || + ( (childValue.isArray() || childValue.isObject()) && + childValue.size() > 0 ); + } + if ( !isMultiLine ) // check if line length > max line length + { + childValues_.reserve( size ); + addChildValues_ = true; + int lineLength = 4 + (size-1)*2; // '[ ' + ', '*n + ' ]' + for ( int index =0; index < size && !isMultiLine; ++index ) + { + writeValue( value[index] ); + lineLength += int( childValues_[index].length() ); + isMultiLine = isMultiLine && hasCommentForValue( value[index] ); + } + addChildValues_ = false; + isMultiLine = isMultiLine || lineLength >= rightMargin_; + } + return isMultiLine; +} + + +void +StyledWriter::pushValue( const std::string &value ) +{ + if ( addChildValues_ ) + childValues_.push_back( value ); + else + document_ += value; +} + + +void +StyledWriter::writeIndent() +{ + if ( !document_.empty() ) + { + char last = document_[document_.length()-1]; + if ( last == ' ' ) // already indented + return; + if ( last != '\n' ) // Comments may add new-line + document_ += '\n'; + } + document_ += indentString_; +} + + +void +StyledWriter::writeWithIndent( const std::string &value ) +{ + writeIndent(); + document_ += value; +} + + +void +StyledWriter::indent() +{ + indentString_ += std::string( indentSize_, ' ' ); +} + + +void +StyledWriter::unindent() +{ + assert( int(indentString_.size()) >= indentSize_ ); + indentString_.resize( indentString_.size() - indentSize_ ); +} + + +void +StyledWriter::writeCommentBeforeValue( const Value &root ) +{ + if ( !root.hasComment( commentBefore ) ) + return; + document_ += normalizeEOL( root.getComment( commentBefore ) ); + document_ += "\n"; +} + + +void +StyledWriter::writeCommentAfterValueOnSameLine( const Value &root ) +{ + if ( root.hasComment( commentAfterOnSameLine ) ) + document_ += " " + normalizeEOL( root.getComment( commentAfterOnSameLine ) ); + + if ( root.hasComment( commentAfter ) ) + { + document_ += "\n"; + document_ += normalizeEOL( root.getComment( commentAfter ) ); + document_ += "\n"; + } +} + + +bool +StyledWriter::hasCommentForValue( const Value &value ) +{ + return value.hasComment( commentBefore ) + || value.hasComment( commentAfterOnSameLine ) + || value.hasComment( commentAfter ); +} + + +std::string +StyledWriter::normalizeEOL( const std::string &text ) +{ + std::string normalized; + normalized.reserve( text.length() ); + const char *begin = text.c_str(); + const char *end = begin + text.length(); + const char *current = begin; + while ( current != end ) + { + char c = *current++; + if ( c == '\r' ) // mac or dos EOL + { + if ( *current == '\n' ) // convert dos EOL + ++current; + normalized += '\n'; + } + else // handle unix EOL & other char + normalized += c; + } + return normalized; +} + + +// Class StyledStreamWriter +// ////////////////////////////////////////////////////////////////// + +StyledStreamWriter::StyledStreamWriter( std::string indentation ) + : document_(NULL) + , rightMargin_( 74 ) + , indentation_( indentation ) + , addChildValues_() +{ +} + + +void +StyledStreamWriter::write( std::ostream &out, const Value &root ) +{ + document_ = &out; + addChildValues_ = false; + indentString_ = ""; + writeCommentBeforeValue( root ); + writeValue( root ); + writeCommentAfterValueOnSameLine( root ); + *document_ << "\n"; + document_ = NULL; // Forget the stream, for safety. +} + + +void +StyledStreamWriter::writeValue( const Value &value ) +{ + switch ( value.type() ) + { + case nullValue: + pushValue( "null" ); + break; + case intValue: + pushValue( valueToString( value.asLargestInt() ) ); + break; + case uintValue: + pushValue( valueToString( value.asLargestUInt() ) ); + break; + case realValue: + pushValue( valueToString( value.asDouble() ) ); + break; + case stringValue: + pushValue( valueToQuotedString( value.asCString() ) ); + break; + case booleanValue: + pushValue( valueToString( value.asBool() ) ); + break; + case arrayValue: + writeArrayValue( value); + break; + case objectValue: + { + Value::Members members( value.getMemberNames() ); + if ( members.empty() ) + pushValue( "{}" ); + else + { + writeWithIndent( "{" ); + indent(); + Value::Members::iterator it = members.begin(); + for (;;) + { + const std::string &name = *it; + const Value &childValue = value[name]; + writeCommentBeforeValue( childValue ); + writeWithIndent( valueToQuotedString( name.c_str() ) ); + *document_ << " : "; + writeValue( childValue ); + if ( ++it == members.end() ) + { + writeCommentAfterValueOnSameLine( childValue ); + break; + } + *document_ << ","; + writeCommentAfterValueOnSameLine( childValue ); + } + unindent(); + writeWithIndent( "}" ); + } + } + break; + } +} + + +void +StyledStreamWriter::writeArrayValue( const Value &value ) +{ + unsigned size = value.size(); + if ( size == 0 ) + pushValue( "[]" ); + else + { + bool isArrayMultiLine = isMultineArray( value ); + if ( isArrayMultiLine ) + { + writeWithIndent( "[" ); + indent(); + bool hasChildValue = !childValues_.empty(); + unsigned index =0; + for (;;) + { + const Value &childValue = value[index]; + writeCommentBeforeValue( childValue ); + if ( hasChildValue ) + writeWithIndent( childValues_[index] ); + else + { + writeIndent(); + writeValue( childValue ); + } + if ( ++index == size ) + { + writeCommentAfterValueOnSameLine( childValue ); + break; + } + *document_ << ","; + writeCommentAfterValueOnSameLine( childValue ); + } + unindent(); + writeWithIndent( "]" ); + } + else // output on a single line + { + assert( childValues_.size() == size ); + *document_ << "[ "; + for ( unsigned index =0; index < size; ++index ) + { + if ( index > 0 ) + *document_ << ", "; + *document_ << childValues_[index]; + } + *document_ << " ]"; + } + } +} + + +bool +StyledStreamWriter::isMultineArray( const Value &value ) +{ + int size = value.size(); + bool isMultiLine = size*3 >= rightMargin_ ; + childValues_.clear(); + for ( int index =0; index < size && !isMultiLine; ++index ) + { + const Value &childValue = value[index]; + isMultiLine = isMultiLine || + ( (childValue.isArray() || childValue.isObject()) && + childValue.size() > 0 ); + } + if ( !isMultiLine ) // check if line length > max line length + { + childValues_.reserve( size ); + addChildValues_ = true; + int lineLength = 4 + (size-1)*2; // '[ ' + ', '*n + ' ]' + for ( int index =0; index < size && !isMultiLine; ++index ) + { + writeValue( value[index] ); + lineLength += int( childValues_[index].length() ); + isMultiLine = isMultiLine && hasCommentForValue( value[index] ); + } + addChildValues_ = false; + isMultiLine = isMultiLine || lineLength >= rightMargin_; + } + return isMultiLine; +} + + +void +StyledStreamWriter::pushValue( const std::string &value ) +{ + if ( addChildValues_ ) + childValues_.push_back( value ); + else + *document_ << value; +} + + +void +StyledStreamWriter::writeIndent() +{ + /* + Some comments in this method would have been nice. ;-) + + if ( !document_.empty() ) + { + char last = document_[document_.length()-1]; + if ( last == ' ' ) // already indented + return; + if ( last != '\n' ) // Comments may add new-line + *document_ << '\n'; + } + */ + *document_ << '\n' << indentString_; +} + + +void +StyledStreamWriter::writeWithIndent( const std::string &value ) +{ + writeIndent(); + *document_ << value; +} + + +void +StyledStreamWriter::indent() +{ + indentString_ += indentation_; +} + + +void +StyledStreamWriter::unindent() +{ + assert( indentString_.size() >= indentation_.size() ); + indentString_.resize( indentString_.size() - indentation_.size() ); +} + + +void +StyledStreamWriter::writeCommentBeforeValue( const Value &root ) +{ + if ( !root.hasComment( commentBefore ) ) + return; + *document_ << normalizeEOL( root.getComment( commentBefore ) ); + *document_ << "\n"; +} + + +void +StyledStreamWriter::writeCommentAfterValueOnSameLine( const Value &root ) +{ + if ( root.hasComment( commentAfterOnSameLine ) ) + *document_ << " " + normalizeEOL( root.getComment( commentAfterOnSameLine ) ); + + if ( root.hasComment( commentAfter ) ) + { + *document_ << "\n"; + *document_ << normalizeEOL( root.getComment( commentAfter ) ); + *document_ << "\n"; + } +} + + +bool +StyledStreamWriter::hasCommentForValue( const Value &value ) +{ + return value.hasComment( commentBefore ) + || value.hasComment( commentAfterOnSameLine ) + || value.hasComment( commentAfter ); +} + + +std::string +StyledStreamWriter::normalizeEOL( const std::string &text ) +{ + std::string normalized; + normalized.reserve( text.length() ); + const char *begin = text.c_str(); + const char *end = begin + text.length(); + const char *current = begin; + while ( current != end ) + { + char c = *current++; + if ( c == '\r' ) // mac or dos EOL + { + if ( *current == '\n' ) // convert dos EOL + ++current; + normalized += '\n'; + } + else // handle unix EOL & other char + normalized += c; + } + return normalized; +} + + +std::ostream& operator<<( std::ostream &sout, const Value &root ) +{ + Json::StyledStreamWriter writer; + writer.write(sout, root); + return sout; +} + + +} // namespace Json
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/third_party/json/reader.h Thu Aug 25 11:05:55 2011 +0100 @@ -0,0 +1,214 @@ +// Copyright 2007-2010 Baptiste Lepilleur +// Distributed under MIT license, or public domain if desired and +// recognized in your jurisdiction. +// See file LICENSE for detail or copy at http://jsoncpp.sourceforge.net/LICENSE + +#ifndef CPPTL_JSON_READER_H_INCLUDED +# define CPPTL_JSON_READER_H_INCLUDED + +#if !defined(JSON_IS_AMALGAMATION) +# include "features.h" +# include "value.h" +#endif // if !defined(JSON_IS_AMALGAMATION) +# include <deque> +# include <stack> +# include <string> +# include <iostream> + +namespace Json { + + /** \brief Unserialize a <a HREF="http://www.json.org">JSON</a> document into a Value. + * + */ + class JSON_API Reader + { + public: + typedef char Char; + typedef const Char *Location; + + /** \brief Constructs a Reader allowing all features + * for parsing. + */ + Reader(); + + /** \brief Constructs a Reader allowing the specified feature set + * for parsing. + */ + Reader( const Features &features ); + + /** \brief Read a Value from a <a HREF="http://www.json.org">JSON</a> document. + * \param document UTF-8 encoded string containing the document to read. + * \param root [out] Contains the root value of the document if it was + * successfully parsed. + * \param collectComments \c true to collect comment and allow writing them back during + * serialization, \c false to discard comments. + * This parameter is ignored if Features::allowComments_ + * is \c false. + * \return \c true if the document was successfully parsed, \c false if an error occurred. + */ + bool parse( const std::string &document, + Value &root, + bool collectComments = true ); + + /** \brief Read a Value from a <a HREF="http://www.json.org">JSON</a> document. + * \param beginDoc Pointer on the beginning of the UTF-8 encoded string of the document to read. + * \param endDoc Pointer on the end of the UTF-8 encoded string of the document to read. + \ Must be >= beginDoc. + * \param root [out] Contains the root value of the document if it was + * successfully parsed. + * \param collectComments \c true to collect comment and allow writing them back during + * serialization, \c false to discard comments. + * This parameter is ignored if Features::allowComments_ + * is \c false. + * \return \c true if the document was successfully parsed, \c false if an error occurred. + */ + bool parse( const char *beginDoc, const char *endDoc, + Value &root, + bool collectComments = true ); + + /// \brief Parse from input stream. + /// \see Json::operator>>(std::istream&, Json::Value&). + bool parse( std::istream &is, + Value &root, + bool collectComments = true ); + + /** \brief Returns a user friendly string that list errors in the parsed document. + * \return Formatted error message with the list of errors with their location in + * the parsed document. An empty string is returned if no error occurred + * during parsing. + * \deprecated Use getFormattedErrorMessages() instead (typo fix). + */ + JSONCPP_DEPRECATED("Use getFormattedErrorMessages instead") + std::string getFormatedErrorMessages() const; + + /** \brief Returns a user friendly string that list errors in the parsed document. + * \return Formatted error message with the list of errors with their location in + * the parsed document. An empty string is returned if no error occurred + * during parsing. + */ + std::string getFormattedErrorMessages() const; + + private: + enum TokenType + { + tokenEndOfStream = 0, + tokenObjectBegin, + tokenObjectEnd, + tokenArrayBegin, + tokenArrayEnd, + tokenString, + tokenNumber, + tokenTrue, + tokenFalse, + tokenNull, + tokenArraySeparator, + tokenMemberSeparator, + tokenComment, + tokenError + }; + + class Token + { + public: + TokenType type_; + Location start_; + Location end_; + }; + + class ErrorInfo + { + public: + Token token_; + std::string message_; + Location extra_; + }; + + typedef std::deque<ErrorInfo> Errors; + + bool expectToken( TokenType type, Token &token, const char *message ); + bool readToken( Token &token ); + void skipSpaces(); + bool match( Location pattern, + int patternLength ); + bool readComment(); + bool readCStyleComment(); + bool readCppStyleComment(); + bool readString(); + void readNumber(); + bool readValue(); + bool readObject( Token &token ); + bool readArray( Token &token ); + bool decodeNumber( Token &token ); + bool decodeString( Token &token ); + bool decodeString( Token &token, std::string &decoded ); + bool decodeDouble( Token &token ); + bool decodeUnicodeCodePoint( Token &token, + Location ¤t, + Location end, + unsigned int &unicode ); + bool decodeUnicodeEscapeSequence( Token &token, + Location ¤t, + Location end, + unsigned int &unicode ); + bool addError( const std::string &message, + Token &token, + Location extra = 0 ); + bool recoverFromError( TokenType skipUntilToken ); + bool addErrorAndRecover( const std::string &message, + Token &token, + TokenType skipUntilToken ); + void skipUntilSpace(); + Value ¤tValue(); + Char getNextChar(); + void getLocationLineAndColumn( Location location, + int &line, + int &column ) const; + std::string getLocationLineAndColumn( Location location ) const; + void addComment( Location begin, + Location end, + CommentPlacement placement ); + void skipCommentTokens( Token &token ); + + typedef std::stack<Value *> Nodes; + Nodes nodes_; + Errors errors_; + std::string document_; + Location begin_; + Location end_; + Location current_; + Location lastValueEnd_; + Value *lastValue_; + std::string commentsBefore_; + Features features_; + bool collectComments_; + }; + + /** \brief Read from 'sin' into 'root'. + + Always keep comments from the input JSON. + + This can be used to read a file into a particular sub-object. + For example: + \code + Json::Value root; + cin >> root["dir"]["file"]; + cout << root; + \endcode + Result: + \verbatim + { + "dir": { + "file": { + // The input stream JSON would be nested here. + } + } + } + \endverbatim + \throw std::exception on parse error. + \see Json::operator<<() + */ + std::istream& operator>>( std::istream&, Value& ); + +} // namespace Json + +#endif // CPPTL_JSON_READER_H_INCLUDED
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/third_party/json/value.h Thu Aug 25 11:05:55 2011 +0100 @@ -0,0 +1,1109 @@ +// Copyright 2007-2010 Baptiste Lepilleur +// Distributed under MIT license, or public domain if desired and +// recognized in your jurisdiction. +// See file LICENSE for detail or copy at http://jsoncpp.sourceforge.net/LICENSE + +#ifndef CPPTL_JSON_H_INCLUDED +# define CPPTL_JSON_H_INCLUDED + +#if !defined(JSON_IS_AMALGAMATION) +# include "forwards.h" +#endif // if !defined(JSON_IS_AMALGAMATION) +# include <string> +# include <vector> + +# ifndef JSON_USE_CPPTL_SMALLMAP +# include <map> +# else +# include <cpptl/smallmap.h> +# endif +# ifdef JSON_USE_CPPTL +# include <cpptl/forwards.h> +# endif + +/** \brief JSON (JavaScript Object Notation). + */ +namespace Json { + + /** \brief Type of the value held by a Value object. + */ + enum ValueType + { + nullValue = 0, ///< 'null' value + intValue, ///< signed integer value + uintValue, ///< unsigned integer value + realValue, ///< double value + stringValue, ///< UTF-8 string value + booleanValue, ///< bool value + arrayValue, ///< array value (ordered list) + objectValue ///< object value (collection of name/value pairs). + }; + + enum CommentPlacement + { + commentBefore = 0, ///< a comment placed on the line before a value + commentAfterOnSameLine, ///< a comment just after a value on the same line + commentAfter, ///< a comment on the line after a value (only make sense for root value) + numberOfCommentPlacement + }; + +//# ifdef JSON_USE_CPPTL +// typedef CppTL::AnyEnumerator<const char *> EnumMemberNames; +// typedef CppTL::AnyEnumerator<const Value &> EnumValues; +//# endif + + /** \brief Lightweight wrapper to tag static string. + * + * Value constructor and objectValue member assignement takes advantage of the + * StaticString and avoid the cost of string duplication when storing the + * string or the member name. + * + * Example of usage: + * \code + * Json::Value aValue( StaticString("some text") ); + * Json::Value object; + * static const StaticString code("code"); + * object[code] = 1234; + * \endcode + */ + class JSON_API StaticString + { + public: + explicit StaticString( const char *czstring ) + : str_( czstring ) + { + } + + operator const char *() const + { + return str_; + } + + const char *c_str() const + { + return str_; + } + + private: + const char *str_; + }; + + /** \brief Represents a <a HREF="http://www.json.org">JSON</a> value. + * + * This class is a discriminated union wrapper that can represents a: + * - signed integer [range: Value::minInt - Value::maxInt] + * - unsigned integer (range: 0 - Value::maxUInt) + * - double + * - UTF-8 string + * - boolean + * - 'null' + * - an ordered list of Value + * - collection of name/value pairs (javascript object) + * + * The type of the held value is represented by a #ValueType and + * can be obtained using type(). + * + * values of an #objectValue or #arrayValue can be accessed using operator[]() methods. + * Non const methods will automatically create the a #nullValue element + * if it does not exist. + * The sequence of an #arrayValue will be automatically resize and initialized + * with #nullValue. resize() can be used to enlarge or truncate an #arrayValue. + * + * The get() methods can be used to obtanis default value in the case the required element + * does not exist. + * + * It is possible to iterate over the list of a #objectValue values using + * the getMemberNames() method. + */ + class JSON_API Value + { + friend class ValueIteratorBase; +# ifdef JSON_VALUE_USE_INTERNAL_MAP + friend class ValueInternalLink; + friend class ValueInternalMap; +# endif + public: + typedef std::vector<std::string> Members; + typedef ValueIterator iterator; + typedef ValueConstIterator const_iterator; + typedef Json::UInt UInt; + typedef Json::Int Int; +# if defined(JSON_HAS_INT64) + typedef Json::UInt64 UInt64; + typedef Json::Int64 Int64; +#endif // defined(JSON_HAS_INT64) + typedef Json::LargestInt LargestInt; + typedef Json::LargestUInt LargestUInt; + typedef Json::ArrayIndex ArrayIndex; + + static const Value null; + /// Minimum signed integer value that can be stored in a Json::Value. + static const LargestInt minLargestInt; + /// Maximum signed integer value that can be stored in a Json::Value. + static const LargestInt maxLargestInt; + /// Maximum unsigned integer value that can be stored in a Json::Value. + static const LargestUInt maxLargestUInt; + + /// Minimum signed int value that can be stored in a Json::Value. + static const Int minInt; + /// Maximum signed int value that can be stored in a Json::Value. + static const Int maxInt; + /// Maximum unsigned int value that can be stored in a Json::Value. + static const UInt maxUInt; + +# if defined(JSON_HAS_INT64) + /// Minimum signed 64 bits int value that can be stored in a Json::Value. + static const Int64 minInt64; + /// Maximum signed 64 bits int value that can be stored in a Json::Value. + static const Int64 maxInt64; + /// Maximum unsigned 64 bits int value that can be stored in a Json::Value. + static const UInt64 maxUInt64; +#endif // defined(JSON_HAS_INT64) + + private: +#ifndef JSONCPP_DOC_EXCLUDE_IMPLEMENTATION +# ifndef JSON_VALUE_USE_INTERNAL_MAP + class CZString + { + public: + enum DuplicationPolicy + { + noDuplication = 0, + duplicate, + duplicateOnCopy + }; + CZString( ArrayIndex index ); + CZString( const char *cstr, DuplicationPolicy allocate ); + CZString( const CZString &other ); + ~CZString(); + CZString &operator =( const CZString &other ); + bool operator<( const CZString &other ) const; + bool operator==( const CZString &other ) const; + ArrayIndex index() const; + const char *c_str() const; + bool isStaticString() const; + private: + void swap( CZString &other ); + const char *cstr_; + ArrayIndex index_; + }; + + public: +# ifndef JSON_USE_CPPTL_SMALLMAP + typedef std::map<CZString, Value> ObjectValues; +# else + typedef CppTL::SmallMap<CZString, Value> ObjectValues; +# endif // ifndef JSON_USE_CPPTL_SMALLMAP +# endif // ifndef JSON_VALUE_USE_INTERNAL_MAP +#endif // ifndef JSONCPP_DOC_EXCLUDE_IMPLEMENTATION + + public: + /** \brief Create a default Value of the given type. + + This is a very useful constructor. + To create an empty array, pass arrayValue. + To create an empty object, pass objectValue. + Another Value can then be set to this one by assignment. + This is useful since clear() and resize() will not alter types. + + Examples: + \code + Json::Value null_value; // null + Json::Value arr_value(Json::arrayValue); // [] + Json::Value obj_value(Json::objectValue); // {} + \endcode + */ + Value( ValueType type = nullValue ); + Value( Int value ); + Value( UInt value ); +#if defined(JSON_HAS_INT64) + Value( Int64 value ); + Value( UInt64 value ); +#endif // if defined(JSON_HAS_INT64) + Value( double value ); + Value( const char *value ); + Value( const char *beginValue, const char *endValue ); + /** \brief Constructs a value from a static string. + + * Like other value string constructor but do not duplicate the string for + * internal storage. The given string must remain alive after the call to this + * constructor. + * Example of usage: + * \code + * Json::Value aValue( StaticString("some text") ); + * \endcode + */ + Value( const StaticString &value ); + Value( const std::string &value ); +# ifdef JSON_USE_CPPTL + Value( const CppTL::ConstString &value ); +# endif + Value( bool value ); + Value( const Value &other ); + ~Value(); + + Value &operator=( const Value &other ); + /// Swap values. + /// \note Currently, comments are intentionally not swapped, for + /// both logic and efficiency. + void swap( Value &other ); + + ValueType type() const; + + bool operator <( const Value &other ) const; + bool operator <=( const Value &other ) const; + bool operator >=( const Value &other ) const; + bool operator >( const Value &other ) const; + + bool operator ==( const Value &other ) const; + bool operator !=( const Value &other ) const; + + int compare( const Value &other ) const; + + const char *asCString() const; + std::string asString() const; +# ifdef JSON_USE_CPPTL + CppTL::ConstString asConstString() const; +# endif + Int asInt() const; + UInt asUInt() const; +#if defined(JSON_HAS_INT64) + Int64 asInt64() const; + UInt64 asUInt64() const; +#endif // if defined(JSON_HAS_INT64) + LargestInt asLargestInt() const; + LargestUInt asLargestUInt() const; + float asFloat() const; + double asDouble() const; + bool asBool() const; + + bool isNull() const; + bool isBool() const; + bool isInt() const; + bool isInt64() const; + bool isUInt() const; + bool isUInt64() const; + bool isIntegral() const; + bool isDouble() const; + bool isNumeric() const; + bool isString() const; + bool isArray() const; + bool isObject() const; + + bool isConvertibleTo( ValueType other ) const; + + /// Number of values in array or object + ArrayIndex size() const; + + /// \brief Return true if empty array, empty object, or null; + /// otherwise, false. + bool empty() const; + + /// Return isNull() + bool operator!() const; + + /// Remove all object members and array elements. + /// \pre type() is arrayValue, objectValue, or nullValue + /// \post type() is unchanged + void clear(); + + /// Resize the array to size elements. + /// New elements are initialized to null. + /// May only be called on nullValue or arrayValue. + /// \pre type() is arrayValue or nullValue + /// \post type() is arrayValue + void resize( ArrayIndex size ); + + /// Access an array element (zero based index ). + /// If the array contains less than index element, then null value are inserted + /// in the array so that its size is index+1. + /// (You may need to say 'value[0u]' to get your compiler to distinguish + /// this from the operator[] which takes a string.) + Value &operator[]( ArrayIndex index ); + + /// Access an array element (zero based index ). + /// If the array contains less than index element, then null value are inserted + /// in the array so that its size is index+1. + /// (You may need to say 'value[0u]' to get your compiler to distinguish + /// this from the operator[] which takes a string.) + Value &operator[]( int index ); + + /// Access an array element (zero based index ) + /// (You may need to say 'value[0u]' to get your compiler to distinguish + /// this from the operator[] which takes a string.) + const Value &operator[]( ArrayIndex index ) const; + + /// Access an array element (zero based index ) + /// (You may need to say 'value[0u]' to get your compiler to distinguish + /// this from the operator[] which takes a string.) + const Value &operator[]( int index ) const; + + /// If the array contains at least index+1 elements, returns the element value, + /// otherwise returns defaultValue. + Value get( ArrayIndex index, + const Value &defaultValue ) const; + /// Return true if index < size(). + bool isValidIndex( ArrayIndex index ) const; + /// \brief Append value to array at the end. + /// + /// Equivalent to jsonvalue[jsonvalue.size()] = value; + Value &append( const Value &value ); + + /// Access an object value by name, create a null member if it does not exist. + Value &operator[]( const char *key ); + /// Access an object value by name, returns null if there is no member with that name. + const Value &operator[]( const char *key ) const; + /// Access an object value by name, create a null member if it does not exist. + Value &operator[]( const std::string &key ); + /// Access an object value by name, returns null if there is no member with that name. + const Value &operator[]( const std::string &key ) const; + /** \brief Access an object value by name, create a null member if it does not exist. + + * If the object as no entry for that name, then the member name used to store + * the new entry is not duplicated. + * Example of use: + * \code + * Json::Value object; + * static const StaticString code("code"); + * object[code] = 1234; + * \endcode + */ + Value &operator[]( const StaticString &key ); +# ifdef JSON_USE_CPPTL + /// Access an object value by name, create a null member if it does not exist. + Value &operator[]( const CppTL::ConstString &key ); + /// Access an object value by name, returns null if there is no member with that name. + const Value &operator[]( const CppTL::ConstString &key ) const; +# endif + /// Return the member named key if it exist, defaultValue otherwise. + Value get( const char *key, + const Value &defaultValue ) const; + /// Return the member named key if it exist, defaultValue otherwise. + Value get( const std::string &key, + const Value &defaultValue ) const; +# ifdef JSON_USE_CPPTL + /// Return the member named key if it exist, defaultValue otherwise. + Value get( const CppTL::ConstString &key, + const Value &defaultValue ) const; +# endif + /// \brief Remove and return the named member. + /// + /// Do nothing if it did not exist. + /// \return the removed Value, or null. + /// \pre type() is objectValue or nullValue + /// \post type() is unchanged + Value removeMember( const char* key ); + /// Same as removeMember(const char*) + Value removeMember( const std::string &key ); + + /// Return true if the object has a member named key. + bool isMember( const char *key ) const; + /// Return true if the object has a member named key. + bool isMember( const std::string &key ) const; +# ifdef JSON_USE_CPPTL + /// Return true if the object has a member named key. + bool isMember( const CppTL::ConstString &key ) const; +# endif + + /// \brief Return a list of the member names. + /// + /// If null, return an empty list. + /// \pre type() is objectValue or nullValue + /// \post if type() was nullValue, it remains nullValue + Members getMemberNames() const; + +//# ifdef JSON_USE_CPPTL +// EnumMemberNames enumMemberNames() const; +// EnumValues enumValues() const; +//# endif + + /// Comments must be //... or /* ... */ + void setComment( const char *comment, + CommentPlacement placement ); + /// Comments must be //... or /* ... */ + void setComment( const std::string &comment, + CommentPlacement placement ); + bool hasComment( CommentPlacement placement ) const; + /// Include delimiters and embedded newlines. + std::string getComment( CommentPlacement placement ) const; + + std::string toStyledString() const; + + const_iterator begin() const; + const_iterator end() const; + + iterator begin(); + iterator end(); + + private: + Value &resolveReference( const char *key, + bool isStatic ); + +# ifdef JSON_VALUE_USE_INTERNAL_MAP + inline bool isItemAvailable() const + { + return itemIsUsed_ == 0; + } + + inline void setItemUsed( bool isUsed = true ) + { + itemIsUsed_ = isUsed ? 1 : 0; + } + + inline bool isMemberNameStatic() const + { + return memberNameIsStatic_ == 0; + } + + inline void setMemberNameIsStatic( bool isStatic ) + { + memberNameIsStatic_ = isStatic ? 1 : 0; + } +# endif // # ifdef JSON_VALUE_USE_INTERNAL_MAP + + private: + struct CommentInfo + { + CommentInfo(); + ~CommentInfo(); + + void setComment( const char *text ); + + char *comment_; + }; + + //struct MemberNamesTransform + //{ + // typedef const char *result_type; + // const char *operator()( const CZString &name ) const + // { + // return name.c_str(); + // } + //}; + + union ValueHolder + { + LargestInt int_; + LargestUInt uint_; + double real_; + bool bool_; + char *string_; +# ifdef JSON_VALUE_USE_INTERNAL_MAP + ValueInternalArray *array_; + ValueInternalMap *map_; +#else + ObjectValues *map_; +# endif + } value_; + ValueType type_ : 8; + int allocated_ : 1; // Notes: if declared as bool, bitfield is useless. +# ifdef JSON_VALUE_USE_INTERNAL_MAP + unsigned int itemIsUsed_ : 1; // used by the ValueInternalMap container. + int memberNameIsStatic_ : 1; // used by the ValueInternalMap container. +# endif + CommentInfo *comments_; + }; + + + /** \brief Experimental and untested: represents an element of the "path" to access a node. + */ + class PathArgument + { + public: + friend class Path; + + PathArgument(); + PathArgument( ArrayIndex index ); + PathArgument( const char *key ); + PathArgument( const std::string &key ); + + private: + enum Kind + { + kindNone = 0, + kindIndex, + kindKey + }; + std::string key_; + ArrayIndex index_; + Kind kind_; + }; + + /** \brief Experimental and untested: represents a "path" to access a node. + * + * Syntax: + * - "." => root node + * - ".[n]" => elements at index 'n' of root node (an array value) + * - ".name" => member named 'name' of root node (an object value) + * - ".name1.name2.name3" + * - ".[0][1][2].name1[3]" + * - ".%" => member name is provided as parameter + * - ".[%]" => index is provied as parameter + */ + class Path + { + public: + Path( const std::string &path, + const PathArgument &a1 = PathArgument(), + const PathArgument &a2 = PathArgument(), + const PathArgument &a3 = PathArgument(), + const PathArgument &a4 = PathArgument(), + const PathArgument &a5 = PathArgument() ); + + const Value &resolve( const Value &root ) const; + Value resolve( const Value &root, + const Value &defaultValue ) const; + /// Creates the "path" to access the specified node and returns a reference on the node. + Value &make( Value &root ) const; + + private: + typedef std::vector<const PathArgument *> InArgs; + typedef std::vector<PathArgument> Args; + + void makePath( const std::string &path, + const InArgs &in ); + void addPathInArg( const std::string &path, + const InArgs &in, + InArgs::const_iterator &itInArg, + PathArgument::Kind kind ); + void invalidPath( const std::string &path, + int location ); + + Args args_; + }; + + + +#ifdef JSON_VALUE_USE_INTERNAL_MAP + /** \brief Allocator to customize Value internal map. + * Below is an example of a simple implementation (default implementation actually + * use memory pool for speed). + * \code + class DefaultValueMapAllocator : public ValueMapAllocator + { + public: // overridden from ValueMapAllocator + virtual ValueInternalMap *newMap() + { + return new ValueInternalMap(); + } + + virtual ValueInternalMap *newMapCopy( const ValueInternalMap &other ) + { + return new ValueInternalMap( other ); + } + + virtual void destructMap( ValueInternalMap *map ) + { + delete map; + } + + virtual ValueInternalLink *allocateMapBuckets( unsigned int size ) + { + return new ValueInternalLink[size]; + } + + virtual void releaseMapBuckets( ValueInternalLink *links ) + { + delete [] links; + } + + virtual ValueInternalLink *allocateMapLink() + { + return new ValueInternalLink(); + } + + virtual void releaseMapLink( ValueInternalLink *link ) + { + delete link; + } + }; + * \endcode + */ + class JSON_API ValueMapAllocator + { + public: + virtual ~ValueMapAllocator(); + virtual ValueInternalMap *newMap() = 0; + virtual ValueInternalMap *newMapCopy( const ValueInternalMap &other ) = 0; + virtual void destructMap( ValueInternalMap *map ) = 0; + virtual ValueInternalLink *allocateMapBuckets( unsigned int size ) = 0; + virtual void releaseMapBuckets( ValueInternalLink *links ) = 0; + virtual ValueInternalLink *allocateMapLink() = 0; + virtual void releaseMapLink( ValueInternalLink *link ) = 0; + }; + + /** \brief ValueInternalMap hash-map bucket chain link (for internal use only). + * \internal previous_ & next_ allows for bidirectional traversal. + */ + class JSON_API ValueInternalLink + { + public: + enum { itemPerLink = 6 }; // sizeof(ValueInternalLink) = 128 on 32 bits architecture. + enum InternalFlags { + flagAvailable = 0, + flagUsed = 1 + }; + + ValueInternalLink(); + + ~ValueInternalLink(); + + Value items_[itemPerLink]; + char *keys_[itemPerLink]; + ValueInternalLink *previous_; + ValueInternalLink *next_; + }; + + + /** \brief A linked page based hash-table implementation used internally by Value. + * \internal ValueInternalMap is a tradional bucket based hash-table, with a linked + * list in each bucket to handle collision. There is an addional twist in that + * each node of the collision linked list is a page containing a fixed amount of + * value. This provides a better compromise between memory usage and speed. + * + * Each bucket is made up of a chained list of ValueInternalLink. The last + * link of a given bucket can be found in the 'previous_' field of the following bucket. + * The last link of the last bucket is stored in tailLink_ as it has no following bucket. + * Only the last link of a bucket may contains 'available' item. The last link always + * contains at least one element unless is it the bucket one very first link. + */ + class JSON_API ValueInternalMap + { + friend class ValueIteratorBase; + friend class Value; + public: + typedef unsigned int HashKey; + typedef unsigned int BucketIndex; + +# ifndef JSONCPP_DOC_EXCLUDE_IMPLEMENTATION + struct IteratorState + { + IteratorState() + : map_(0) + , link_(0) + , itemIndex_(0) + , bucketIndex_(0) + { + } + ValueInternalMap *map_; + ValueInternalLink *link_; + BucketIndex itemIndex_; + BucketIndex bucketIndex_; + }; +# endif // ifndef JSONCPP_DOC_EXCLUDE_IMPLEMENTATION + + ValueInternalMap(); + ValueInternalMap( const ValueInternalMap &other ); + ValueInternalMap &operator =( const ValueInternalMap &other ); + ~ValueInternalMap(); + + void swap( ValueInternalMap &other ); + + BucketIndex size() const; + + void clear(); + + bool reserveDelta( BucketIndex growth ); + + bool reserve( BucketIndex newItemCount ); + + const Value *find( const char *key ) const; + + Value *find( const char *key ); + + Value &resolveReference( const char *key, + bool isStatic ); + + void remove( const char *key ); + + void doActualRemove( ValueInternalLink *link, + BucketIndex index, + BucketIndex bucketIndex ); + + ValueInternalLink *&getLastLinkInBucket( BucketIndex bucketIndex ); + + Value &setNewItem( const char *key, + bool isStatic, + ValueInternalLink *link, + BucketIndex index ); + + Value &unsafeAdd( const char *key, + bool isStatic, + HashKey hashedKey ); + + HashKey hash( const char *key ) const; + + int compare( const ValueInternalMap &other ) const; + + private: + void makeBeginIterator( IteratorState &it ) const; + void makeEndIterator( IteratorState &it ) const; + static bool equals( const IteratorState &x, const IteratorState &other ); + static void increment( IteratorState &iterator ); + static void incrementBucket( IteratorState &iterator ); + static void decrement( IteratorState &iterator ); + static const char *key( const IteratorState &iterator ); + static const char *key( const IteratorState &iterator, bool &isStatic ); + static Value &value( const IteratorState &iterator ); + static int distance( const IteratorState &x, const IteratorState &y ); + + private: + ValueInternalLink *buckets_; + ValueInternalLink *tailLink_; + BucketIndex bucketsSize_; + BucketIndex itemCount_; + }; + + /** \brief A simplified deque implementation used internally by Value. + * \internal + * It is based on a list of fixed "page", each page contains a fixed number of items. + * Instead of using a linked-list, a array of pointer is used for fast item look-up. + * Look-up for an element is as follow: + * - compute page index: pageIndex = itemIndex / itemsPerPage + * - look-up item in page: pages_[pageIndex][itemIndex % itemsPerPage] + * + * Insertion is amortized constant time (only the array containing the index of pointers + * need to be reallocated when items are appended). + */ + class JSON_API ValueInternalArray + { + friend class Value; + friend class ValueIteratorBase; + public: + enum { itemsPerPage = 8 }; // should be a power of 2 for fast divide and modulo. + typedef Value::ArrayIndex ArrayIndex; + typedef unsigned int PageIndex; + +# ifndef JSONCPP_DOC_EXCLUDE_IMPLEMENTATION + struct IteratorState // Must be a POD + { + IteratorState() + : array_(0) + , currentPageIndex_(0) + , currentItemIndex_(0) + { + } + ValueInternalArray *array_; + Value **currentPageIndex_; + unsigned int currentItemIndex_; + }; +# endif // ifndef JSONCPP_DOC_EXCLUDE_IMPLEMENTATION + + ValueInternalArray(); + ValueInternalArray( const ValueInternalArray &other ); + ValueInternalArray &operator =( const ValueInternalArray &other ); + ~ValueInternalArray(); + void swap( ValueInternalArray &other ); + + void clear(); + void resize( ArrayIndex newSize ); + + Value &resolveReference( ArrayIndex index ); + + Value *find( ArrayIndex index ) const; + + ArrayIndex size() const; + + int compare( const ValueInternalArray &other ) const; + + private: + static bool equals( const IteratorState &x, const IteratorState &other ); + static void increment( IteratorState &iterator ); + static void decrement( IteratorState &iterator ); + static Value &dereference( const IteratorState &iterator ); + static Value &unsafeDereference( const IteratorState &iterator ); + static int distance( const IteratorState &x, const IteratorState &y ); + static ArrayIndex indexOf( const IteratorState &iterator ); + void makeBeginIterator( IteratorState &it ) const; + void makeEndIterator( IteratorState &it ) const; + void makeIterator( IteratorState &it, ArrayIndex index ) const; + + void makeIndexValid( ArrayIndex index ); + + Value **pages_; + ArrayIndex size_; + PageIndex pageCount_; + }; + + /** \brief Experimental: do not use. Allocator to customize Value internal array. + * Below is an example of a simple implementation (actual implementation use + * memory pool). + \code +class DefaultValueArrayAllocator : public ValueArrayAllocator +{ +public: // overridden from ValueArrayAllocator + virtual ~DefaultValueArrayAllocator() + { + } + + virtual ValueInternalArray *newArray() + { + return new ValueInternalArray(); + } + + virtual ValueInternalArray *newArrayCopy( const ValueInternalArray &other ) + { + return new ValueInternalArray( other ); + } + + virtual void destruct( ValueInternalArray *array ) + { + delete array; + } + + virtual void reallocateArrayPageIndex( Value **&indexes, + ValueInternalArray::PageIndex &indexCount, + ValueInternalArray::PageIndex minNewIndexCount ) + { + ValueInternalArray::PageIndex newIndexCount = (indexCount*3)/2 + 1; + if ( minNewIndexCount > newIndexCount ) + newIndexCount = minNewIndexCount; + void *newIndexes = realloc( indexes, sizeof(Value*) * newIndexCount ); + if ( !newIndexes ) + throw std::bad_alloc(); + indexCount = newIndexCount; + indexes = static_cast<Value **>( newIndexes ); + } + virtual void releaseArrayPageIndex( Value **indexes, + ValueInternalArray::PageIndex indexCount ) + { + if ( indexes ) + free( indexes ); + } + + virtual Value *allocateArrayPage() + { + return static_cast<Value *>( malloc( sizeof(Value) * ValueInternalArray::itemsPerPage ) ); + } + + virtual void releaseArrayPage( Value *value ) + { + if ( value ) + free( value ); + } +}; + \endcode + */ + class JSON_API ValueArrayAllocator + { + public: + virtual ~ValueArrayAllocator(); + virtual ValueInternalArray *newArray() = 0; + virtual ValueInternalArray *newArrayCopy( const ValueInternalArray &other ) = 0; + virtual void destructArray( ValueInternalArray *array ) = 0; + /** \brief Reallocate array page index. + * Reallocates an array of pointer on each page. + * \param indexes [input] pointer on the current index. May be \c NULL. + * [output] pointer on the new index of at least + * \a minNewIndexCount pages. + * \param indexCount [input] current number of pages in the index. + * [output] number of page the reallocated index can handle. + * \b MUST be >= \a minNewIndexCount. + * \param minNewIndexCount Minimum number of page the new index must be able to + * handle. + */ + virtual void reallocateArrayPageIndex( Value **&indexes, + ValueInternalArray::PageIndex &indexCount, + ValueInternalArray::PageIndex minNewIndexCount ) = 0; + virtual void releaseArrayPageIndex( Value **indexes, + ValueInternalArray::PageIndex indexCount ) = 0; + virtual Value *allocateArrayPage() = 0; + virtual void releaseArrayPage( Value *value ) = 0; + }; +#endif // #ifdef JSON_VALUE_USE_INTERNAL_MAP + + + /** \brief base class for Value iterators. + * + */ + class ValueIteratorBase + { + public: + typedef unsigned int size_t; + typedef int difference_type; + typedef ValueIteratorBase SelfType; + + ValueIteratorBase(); +#ifndef JSON_VALUE_USE_INTERNAL_MAP + explicit ValueIteratorBase( const Value::ObjectValues::iterator ¤t ); +#else + ValueIteratorBase( const ValueInternalArray::IteratorState &state ); + ValueIteratorBase( const ValueInternalMap::IteratorState &state ); +#endif + + bool operator ==( const SelfType &other ) const + { + return isEqual( other ); + } + + bool operator !=( const SelfType &other ) const + { + return !isEqual( other ); + } + + difference_type operator -( const SelfType &other ) const + { + return computeDistance( other ); + } + + /// Return either the index or the member name of the referenced value as a Value. + Value key() const; + + /// Return the index of the referenced Value. -1 if it is not an arrayValue. + UInt index() const; + + /// Return the member name of the referenced Value. "" if it is not an objectValue. + const char *memberName() const; + + protected: + Value &deref() const; + + void increment(); + + void decrement(); + + difference_type computeDistance( const SelfType &other ) const; + + bool isEqual( const SelfType &other ) const; + + void copy( const SelfType &other ); + + private: +#ifndef JSON_VALUE_USE_INTERNAL_MAP + Value::ObjectValues::iterator current_; + // Indicates that iterator is for a null value. + bool isNull_; +#else + union + { + ValueInternalArray::IteratorState array_; + ValueInternalMap::IteratorState map_; + } iterator_; + bool isArray_; +#endif + }; + + /** \brief const iterator for object and array value. + * + */ + class ValueConstIterator : public ValueIteratorBase + { + friend class Value; + public: + typedef unsigned int size_t; + typedef int difference_type; + typedef const Value &reference; + typedef const Value *pointer; + typedef ValueConstIterator SelfType; + + ValueConstIterator(); + private: + /*! \internal Use by Value to create an iterator. + */ +#ifndef JSON_VALUE_USE_INTERNAL_MAP + explicit ValueConstIterator( const Value::ObjectValues::iterator ¤t ); +#else + ValueConstIterator( const ValueInternalArray::IteratorState &state ); + ValueConstIterator( const ValueInternalMap::IteratorState &state ); +#endif + public: + SelfType &operator =( const ValueIteratorBase &other ); + + SelfType operator++( int ) + { + SelfType temp( *this ); + ++*this; + return temp; + } + + SelfType operator--( int ) + { + SelfType temp( *this ); + --*this; + return temp; + } + + SelfType &operator--() + { + decrement(); + return *this; + } + + SelfType &operator++() + { + increment(); + return *this; + } + + reference operator *() const + { + return deref(); + } + }; + + + /** \brief Iterator for object and array value. + */ + class ValueIterator : public ValueIteratorBase + { + friend class Value; + public: + typedef unsigned int size_t; + typedef int difference_type; + typedef Value &reference; + typedef Value *pointer; + typedef ValueIterator SelfType; + + ValueIterator(); + ValueIterator( const ValueConstIterator &other ); + ValueIterator( const ValueIterator &other ); + private: + /*! \internal Use by Value to create an iterator. + */ +#ifndef JSON_VALUE_USE_INTERNAL_MAP + explicit ValueIterator( const Value::ObjectValues::iterator ¤t ); +#else + ValueIterator( const ValueInternalArray::IteratorState &state ); + ValueIterator( const ValueInternalMap::IteratorState &state ); +#endif + public: + + SelfType &operator =( const SelfType &other ); + + SelfType operator++( int ) + { + SelfType temp( *this ); + ++*this; + return temp; + } + + SelfType operator--( int ) + { + SelfType temp( *this ); + --*this; + return temp; + } + + SelfType &operator--() + { + decrement(); + return *this; + } + + SelfType &operator++() + { + increment(); + return *this; + } + + reference operator *() const + { + return deref(); + } + }; + + +} // namespace Json + + +#endif // CPPTL_JSON_H_INCLUDED
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/third_party/json/writer.h Thu Aug 25 11:05:55 2011 +0100 @@ -0,0 +1,185 @@ +// Copyright 2007-2010 Baptiste Lepilleur +// Distributed under MIT license, or public domain if desired and +// recognized in your jurisdiction. +// See file LICENSE for detail or copy at http://jsoncpp.sourceforge.net/LICENSE + +#ifndef JSON_WRITER_H_INCLUDED +# define JSON_WRITER_H_INCLUDED + +#if !defined(JSON_IS_AMALGAMATION) +# include "value.h" +#endif // if !defined(JSON_IS_AMALGAMATION) +# include <vector> +# include <string> +# include <iostream> + +namespace Json { + + class Value; + + /** \brief Abstract class for writers. + */ + class JSON_API Writer + { + public: + virtual ~Writer(); + + virtual std::string write( const Value &root ) = 0; + }; + + /** \brief Outputs a Value in <a HREF="http://www.json.org">JSON</a> format without formatting (not human friendly). + * + * The JSON document is written in a single line. It is not intended for 'human' consumption, + * but may be usefull to support feature such as RPC where bandwith is limited. + * \sa Reader, Value + */ + class JSON_API FastWriter : public Writer + { + public: + FastWriter(); + virtual ~FastWriter(){} + + void enableYAMLCompatibility(); + + public: // overridden from Writer + virtual std::string write( const Value &root ); + + private: + void writeValue( const Value &value ); + + std::string document_; + bool yamlCompatiblityEnabled_; + }; + + /** \brief Writes a Value in <a HREF="http://www.json.org">JSON</a> format in a human friendly way. + * + * The rules for line break and indent are as follow: + * - Object value: + * - if empty then print {} without indent and line break + * - if not empty the print '{', line break & indent, print one value per line + * and then unindent and line break and print '}'. + * - Array value: + * - if empty then print [] without indent and line break + * - if the array contains no object value, empty array or some other value types, + * and all the values fit on one lines, then print the array on a single line. + * - otherwise, it the values do not fit on one line, or the array contains + * object or non empty array, then print one value per line. + * + * If the Value have comments then they are outputed according to their #CommentPlacement. + * + * \sa Reader, Value, Value::setComment() + */ + class JSON_API StyledWriter: public Writer + { + public: + StyledWriter(); + virtual ~StyledWriter(){} + + public: // overridden from Writer + /** \brief Serialize a Value in <a HREF="http://www.json.org">JSON</a> format. + * \param root Value to serialize. + * \return String containing the JSON document that represents the root value. + */ + virtual std::string write( const Value &root ); + + private: + void writeValue( const Value &value ); + void writeArrayValue( const Value &value ); + bool isMultineArray( const Value &value ); + void pushValue( const std::string &value ); + void writeIndent(); + void writeWithIndent( const std::string &value ); + void indent(); + void unindent(); + void writeCommentBeforeValue( const Value &root ); + void writeCommentAfterValueOnSameLine( const Value &root ); + bool hasCommentForValue( const Value &value ); + static std::string normalizeEOL( const std::string &text ); + + typedef std::vector<std::string> ChildValues; + + ChildValues childValues_; + std::string document_; + std::string indentString_; + int rightMargin_; + int indentSize_; + bool addChildValues_; + }; + + /** \brief Writes a Value in <a HREF="http://www.json.org">JSON</a> format in a human friendly way, + to a stream rather than to a string. + * + * The rules for line break and indent are as follow: + * - Object value: + * - if empty then print {} without indent and line break + * - if not empty the print '{', line break & indent, print one value per line + * and then unindent and line break and print '}'. + * - Array value: + * - if empty then print [] without indent and line break + * - if the array contains no object value, empty array or some other value types, + * and all the values fit on one lines, then print the array on a single line. + * - otherwise, it the values do not fit on one line, or the array contains + * object or non empty array, then print one value per line. + * + * If the Value have comments then they are outputed according to their #CommentPlacement. + * + * \param indentation Each level will be indented by this amount extra. + * \sa Reader, Value, Value::setComment() + */ + class JSON_API StyledStreamWriter + { + public: + StyledStreamWriter( std::string indentation="\t" ); + ~StyledStreamWriter(){} + + public: + /** \brief Serialize a Value in <a HREF="http://www.json.org">JSON</a> format. + * \param out Stream to write to. (Can be ostringstream, e.g.) + * \param root Value to serialize. + * \note There is no point in deriving from Writer, since write() should not return a value. + */ + void write( std::ostream &out, const Value &root ); + + private: + void writeValue( const Value &value ); + void writeArrayValue( const Value &value ); + bool isMultineArray( const Value &value ); + void pushValue( const std::string &value ); + void writeIndent(); + void writeWithIndent( const std::string &value ); + void indent(); + void unindent(); + void writeCommentBeforeValue( const Value &root ); + void writeCommentAfterValueOnSameLine( const Value &root ); + bool hasCommentForValue( const Value &value ); + static std::string normalizeEOL( const std::string &text ); + + typedef std::vector<std::string> ChildValues; + + ChildValues childValues_; + std::ostream* document_; + std::string indentString_; + int rightMargin_; + std::string indentation_; + bool addChildValues_; + }; + +# if defined(JSON_HAS_INT64) + std::string JSON_API valueToString( Int value ); + std::string JSON_API valueToString( UInt value ); +# endif // if defined(JSON_HAS_INT64) + std::string JSON_API valueToString( LargestInt value ); + std::string JSON_API valueToString( LargestUInt value ); + std::string JSON_API valueToString( double value ); + std::string JSON_API valueToString( bool value ); + std::string JSON_API valueToQuotedString( const char *value ); + + /// \brief Output using the StyledStreamWriter. + /// \see Json::operator>>() + std::ostream& operator<<( std::ostream&, const Value &root ); + +} // namespace Json + + + +#endif // JSON_WRITER_H_INCLUDED