├── .DS_Store ├── .gitattributes ├── .github └── workflows │ ├── linux.yml │ ├── macos.yml │ └── windows.yml ├── .gitignore ├── .vscode ├── launch.json └── tasks.json ├── LICENSE ├── README.md ├── package.lua ├── src ├── managed.h └── managed │ ├── marray.h │ ├── mhashmap.h │ ├── mlist.h │ └── mstring.h ├── tests ├── .DS_Store ├── src │ ├── alloc.c │ ├── auto.c │ ├── defer.c │ ├── destructor.c │ ├── hashmap.c │ ├── list.c │ ├── main.c │ ├── realloc.c │ ├── string.c │ ├── test.h │ └── threads.c └── xmake.lua └── xmake.lua /.DS_Store: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Frityet/ManagedC/d37d8cbf0900e4cb00204755585885d22fa12754/.DS_Store -------------------------------------------------------------------------------- /.gitattributes: -------------------------------------------------------------------------------- 1 | *.h linguist-language=C 2 | -------------------------------------------------------------------------------- /.github/workflows/linux.yml: -------------------------------------------------------------------------------- 1 | # This is a basic workflow to help you get started with Actions 2 | 3 | name: Linux 4 | 5 | # Controls when the workflow will run 6 | on: 7 | # Triggers the workflow on push or pull request events but only for the "master" branch 8 | push: 9 | branches: ["master"] 10 | pull_request: 11 | branches: ["master"] 12 | 13 | # Allows you to run this workflow manually from the Actions tab 14 | workflow_dispatch: 15 | 16 | # A workflow run is made up of one or more jobs that can run sequentially or in parallel 17 | jobs: 18 | # This workflow contains a single job called "build" 19 | build-and-test: 20 | # The type of runner that the job will run on 21 | runs-on: ubuntu-latest 22 | 23 | # Steps represent a sequence of tasks that will be executed as part of the job 24 | steps: 25 | # Checks-out your repository under $GITHUB_WORKSPACE, so your job can access it 26 | - uses: actions/checkout@v3 27 | 28 | # Runs a single command using the runners shell 29 | - name: setup xmake 30 | uses: xmake-io/github-action-setup-xmake@v1 31 | 32 | - name: configure-debug 33 | run: 34 | xmake config --mode=debug 35 | 36 | - name: build 37 | run: 38 | xmake build -rvwD tests 39 | 40 | - name: test 41 | run: xmake run -vw tests 42 | 43 | - name: configure-release 44 | run: 45 | xmake config --mode=release 46 | 47 | - name: build 48 | run: 49 | xmake build -rvwD tests 50 | 51 | - name: test 52 | run: 53 | xmake run -vw tests 54 | 55 | 56 | -------------------------------------------------------------------------------- /.github/workflows/macos.yml: -------------------------------------------------------------------------------- 1 | # This is a basic workflow to help you get started with Actions 2 | 3 | name: MacOS 4 | 5 | # Controls when the workflow will run 6 | on: 7 | # Triggers the workflow on push or pull request events but only for the "master" branch 8 | push: 9 | branches: ["master"] 10 | pull_request: 11 | branches: ["master"] 12 | 13 | # Allows you to run this workflow manually from the Actions tab 14 | workflow_dispatch: 15 | 16 | # A workflow run is made up of one or more jobs that can run sequentially or in parallel 17 | jobs: 18 | # This workflow contains a single job called "build" 19 | build-and-test: 20 | # The type of runner that the job will run on 21 | runs-on: macos-latest 22 | 23 | # Steps represent a sequence of tasks that will be executed as part of the job 24 | steps: 25 | # Checks-out your repository under $GITHUB_WORKSPACE, so your job can access it 26 | - uses: actions/checkout@v3 27 | 28 | # Runs a single command using the runners shell 29 | - name: setup xmake 30 | uses: xmake-io/github-action-setup-xmake@v1 31 | 32 | - name: configure-debug 33 | run: 34 | xmake config --mode=debug 35 | 36 | - name: build 37 | run: 38 | xmake build -rvwD tests 39 | 40 | - name: test 41 | run: xmake run -vw tests 42 | 43 | - name: configure-release 44 | run: 45 | xmake run -vwD tests 46 | 47 | - name: build 48 | run: 49 | xmake build -rvwD tests 50 | 51 | - name: test 52 | run: 53 | xmake run -vw tests 54 | 55 | 56 | -------------------------------------------------------------------------------- /.github/workflows/windows.yml: -------------------------------------------------------------------------------- 1 | # This is a basic workflow to help you get started with Actions 2 | 3 | name: Windows 4 | 5 | # Controls when the workflow will run 6 | on: 7 | # Triggers the workflow on push or pull request events but only for the "master" branch 8 | push: 9 | branches: ["master"] 10 | pull_request: 11 | branches: ["master"] 12 | 13 | # Allows you to run this workflow manually from the Actions tab 14 | workflow_dispatch: 15 | 16 | # A workflow run is made up of one or more jobs that can run sequentially or in parallel 17 | jobs: 18 | # This workflow contains a single job called "build" 19 | build-and-test: 20 | # The type of runner that the job will run on 21 | runs-on: windows-latest 22 | 23 | # Steps represent a sequence of tasks that will be executed as part of the job 24 | steps: 25 | # Checks-out your repository under $GITHUB_WORKSPACE, so your job can access it 26 | - uses: actions/checkout@v3 27 | 28 | # Runs a single command using the runners shell 29 | - name: setup xmake 30 | uses: xmake-io/github-action-setup-xmake@v1 31 | 32 | - name: configure-debug 33 | run: 34 | xmake config --mode=debug 35 | 36 | - name: build 37 | run: 38 | xmake build -rvwD tests 39 | 40 | - name: test 41 | run: xmake run -vw tests 42 | 43 | - name: configure-release 44 | run: 45 | xmake config --mode=release 46 | 47 | - name: build 48 | run: 49 | xmake build -rvwD tests 50 | 51 | - name: test 52 | run: 53 | xmake run -vw tests 54 | 55 | 56 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | .xmake/ 2 | build/ 3 | 4 | cmake-build-debug/ 5 | cmake-build-debug-clang/ 6 | CMakeLists.txt 7 | 8 | compile_commands.json 9 | 10 | .idea/ 11 | lsp/ 12 | .vscode/ 13 | .cache/ 14 | .clangd/ 15 | 16 | pdf/ 17 | 18 | *.DS_Store 19 | .DS_Store 20 | .DS_Store 21 | 22 | vlatest.c 23 | -------------------------------------------------------------------------------- /.vscode/launch.json: -------------------------------------------------------------------------------- 1 | { 2 | "version": "0.2.0", 3 | "configurations": [ 4 | { 5 | "type": "lldb", 6 | "request": "launch", 7 | "name": "Launch", 8 | "program": "${workspaceRoot}/build/macosx/x86_64/debug/tests", 9 | "args": [ 10 | "all" 11 | ], 12 | "cwd": "${workspaceRoot}/build/macosx/x86_64/debug/", 13 | "console": "internalConsole", 14 | "preLaunchTask": "Build" 15 | } 16 | 17 | ] 18 | } -------------------------------------------------------------------------------- /.vscode/tasks.json: -------------------------------------------------------------------------------- 1 | { 2 | "version": "2.0.0", 3 | "tasks": [ 4 | { 5 | "label": "Build", 6 | "command": "xmake", 7 | "type": "process", 8 | "args": [ 9 | "-v", "-w" 10 | ], 11 | "group": "build" 12 | } 13 | ] 14 | } -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | GNU LESSER GENERAL PUBLIC LICENSE 2 | Version 2.1, February 1999 3 | 4 | Copyright (C) 1991, 1999 Free Software Foundation, Inc. 5 | 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA 6 | Everyone is permitted to copy and distribute verbatim copies 7 | of this license document, but changing it is not allowed. 8 | 9 | [This is the first released version of the Lesser GPL. It also counts 10 | as the successor of the GNU Library Public License, version 2, hence 11 | the version number 2.1.] 12 | 13 | Preamble 14 | 15 | The licenses for most software are designed to take away your 16 | freedom to share and change it. By contrast, the GNU General Public 17 | Licenses are intended to guarantee your freedom to share and change 18 | free software--to make sure the software is free for all its users. 19 | 20 | This license, the Lesser General Public License, applies to some 21 | specially designated software packages--typically libraries--of the 22 | Free Software Foundation and other authors who decide to use it. You 23 | can use it too, but we suggest you first think carefully about whether 24 | this license or the ordinary General Public License is the better 25 | strategy to use in any particular case, based on the explanations below. 26 | 27 | When we speak of free software, we are referring to freedom of use, 28 | not price. Our General Public Licenses are designed to make sure that 29 | you have the freedom to distribute copies of free software (and charge 30 | for this service if you wish); that you receive source code or can get 31 | it if you want it; that you can change the software and use pieces of 32 | it in new free programs; and that you are informed that you can do 33 | these things. 34 | 35 | To protect your rights, we need to make restrictions that forbid 36 | distributors to deny you these rights or to ask you to surrender these 37 | rights. These restrictions translate to certain responsibilities for 38 | you if you distribute copies of the library or if you modify it. 39 | 40 | For example, if you distribute copies of the library, whether gratis 41 | or for a fee, you must give the recipients all the rights that we gave 42 | you. You must make sure that they, too, receive or can get the source 43 | code. If you link other code with the library, you must provide 44 | complete object files to the recipients, so that they can relink them 45 | with the library after making changes to the library and recompiling 46 | it. And you must show them these terms so they know their rights. 47 | 48 | We protect your rights with a two-step method: (1) we copyright the 49 | library, and (2) we offer you this license, which gives you legal 50 | permission to copy, distribute and/or modify the library. 51 | 52 | To protect each distributor, we want to make it very clear that 53 | there is no warranty for the free library. Also, if the library is 54 | modified by someone else and passed on, the recipients should know 55 | that what they have is not the original version, so that the original 56 | author's reputation will not be affected by problems that might be 57 | introduced by others. 58 | 59 | Finally, software patents pose a constant threat to the existence of 60 | any free program. We wish to make sure that a company cannot 61 | effectively restrict the users of a free program by obtaining a 62 | restrictive license from a patent holder. Therefore, we insist that 63 | any patent license obtained for a version of the library must be 64 | consistent with the full freedom of use specified in this license. 65 | 66 | Most GNU software, including some libraries, is covered by the 67 | ordinary GNU General Public License. This license, the GNU Lesser 68 | General Public License, applies to certain designated libraries, and 69 | is quite different from the ordinary General Public License. We use 70 | this license for certain libraries in order to permit linking those 71 | libraries into non-free programs. 72 | 73 | When a program is linked with a library, whether statically or using 74 | a shared library, the combination of the two is legally speaking a 75 | combined work, a derivative of the original library. The ordinary 76 | General Public License therefore permits such linking only if the 77 | entire combination fits its criteria of freedom. The Lesser General 78 | Public License permits more lax criteria for linking other code with 79 | the library. 80 | 81 | We call this license the "Lesser" General Public License because it 82 | does Less to protect the user's freedom than the ordinary General 83 | Public License. It also provides other free software developers Less 84 | of an advantage over competing non-free programs. These disadvantages 85 | are the reason we use the ordinary General Public License for many 86 | libraries. However, the Lesser license provides advantages in certain 87 | special circumstances. 88 | 89 | For example, on rare occasions, there may be a special need to 90 | encourage the widest possible use of a certain library, so that it becomes 91 | a de-facto standard. To achieve this, non-free programs must be 92 | allowed to use the library. A more frequent case is that a free 93 | library does the same job as widely used non-free libraries. In this 94 | case, there is little to gain by limiting the free library to free 95 | software only, so we use the Lesser General Public License. 96 | 97 | In other cases, permission to use a particular library in non-free 98 | programs enables a greater number of people to use a large body of 99 | free software. For example, permission to use the GNU C Library in 100 | non-free programs enables many more people to use the whole GNU 101 | operating system, as well as its variant, the GNU/Linux operating 102 | system. 103 | 104 | Although the Lesser General Public License is Less protective of the 105 | users' freedom, it does ensure that the user of a program that is 106 | linked with the Library has the freedom and the wherewithal to run 107 | that program using a modified version of the Library. 108 | 109 | The precise terms and conditions for copying, distribution and 110 | modification follow. Pay close attention to the difference between a 111 | "work based on the library" and a "work that uses the library". The 112 | former contains code derived from the library, whereas the latter must 113 | be combined with the library in order to run. 114 | 115 | GNU LESSER GENERAL PUBLIC LICENSE 116 | TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION 117 | 118 | 0. This License Agreement applies to any software library or other 119 | program which contains a notice placed by the copyright holder or 120 | other authorized party saying it may be distributed under the terms of 121 | this Lesser General Public License (also called "this License"). 122 | Each licensee is addressed as "you". 123 | 124 | A "library" means a collection of software functions and/or data 125 | prepared so as to be conveniently linked with application programs 126 | (which use some of those functions and data) to form executables. 127 | 128 | The "Library", below, refers to any such software library or work 129 | which has been distributed under these terms. A "work based on the 130 | Library" means either the Library or any derivative work under 131 | copyright law: that is to say, a work containing the Library or a 132 | portion of it, either verbatim or with modifications and/or translated 133 | straightforwardly into another language. (Hereinafter, translation is 134 | included without limitation in the term "modification".) 135 | 136 | "Source code" for a work means the preferred form of the work for 137 | making modifications to it. For a library, complete source code means 138 | all the source code for all modules it contains, plus any associated 139 | interface definition files, plus the scripts used to control compilation 140 | and installation of the library. 141 | 142 | Activities other than copying, distribution and modification are not 143 | covered by this License; they are outside its scope. The act of 144 | running a program using the Library is not restricted, and output from 145 | such a program is covered only if its contents constitute a work based 146 | on the Library (independent of the use of the Library in a tool for 147 | writing it). Whether that is true depends on what the Library does 148 | and what the program that uses the Library does. 149 | 150 | 1. You may copy and distribute verbatim copies of the Library's 151 | complete source code as you receive it, in any medium, provided that 152 | you conspicuously and appropriately publish on each copy an 153 | appropriate copyright notice and disclaimer of warranty; keep intact 154 | all the notices that refer to this License and to the absence of any 155 | warranty; and distribute a copy of this License along with the 156 | Library. 157 | 158 | You may charge a fee for the physical act of transferring a copy, 159 | and you may at your option offer warranty protection in exchange for a 160 | fee. 161 | 162 | 2. You may modify your copy or copies of the Library or any portion 163 | of it, thus forming a work based on the Library, and copy and 164 | distribute such modifications or work under the terms of Section 1 165 | above, provided that you also meet all of these conditions: 166 | 167 | a) The modified work must itself be a software library. 168 | 169 | b) You must cause the files modified to carry prominent notices 170 | stating that you changed the files and the date of any change. 171 | 172 | c) You must cause the whole of the work to be licensed at no 173 | charge to all third parties under the terms of this License. 174 | 175 | d) If a facility in the modified Library refers to a function or a 176 | table of data to be supplied by an application program that uses 177 | the facility, other than as an argument passed when the facility 178 | is invoked, then you must make a good faith effort to ensure that, 179 | in the event an application does not supply such function or 180 | table, the facility still operates, and performs whatever part of 181 | its purpose remains meaningful. 182 | 183 | (For example, a function in a library to compute square roots has 184 | a purpose that is entirely well-defined independent of the 185 | application. Therefore, Subsection 2d requires that any 186 | application-supplied function or table used by this function must 187 | be optional: if the application does not supply it, the square 188 | root function must still compute square roots.) 189 | 190 | These requirements apply to the modified work as a whole. If 191 | identifiable sections of that work are not derived from the Library, 192 | and can be reasonably considered independent and separate works in 193 | themselves, then this License, and its terms, do not apply to those 194 | sections when you distribute them as separate works. But when you 195 | distribute the same sections as part of a whole which is a work based 196 | on the Library, the distribution of the whole must be on the terms of 197 | this License, whose permissions for other licensees extend to the 198 | entire whole, and thus to each and every part regardless of who wrote 199 | it. 200 | 201 | Thus, it is not the intent of this section to claim rights or contest 202 | your rights to work written entirely by you; rather, the intent is to 203 | exercise the right to control the distribution of derivative or 204 | collective works based on the Library. 205 | 206 | In addition, mere aggregation of another work not based on the Library 207 | with the Library (or with a work based on the Library) on a volume of 208 | a storage or distribution medium does not bring the other work under 209 | the scope of this License. 210 | 211 | 3. You may opt to apply the terms of the ordinary GNU General Public 212 | License instead of this License to a given copy of the Library. To do 213 | this, you must alter all the notices that refer to this License, so 214 | that they refer to the ordinary GNU General Public License, version 2, 215 | instead of to this License. (If a newer version than version 2 of the 216 | ordinary GNU General Public License has appeared, then you can specify 217 | that version instead if you wish.) Do not make any other change in 218 | these notices. 219 | 220 | Once this change is made in a given copy, it is irreversible for 221 | that copy, so the ordinary GNU General Public License applies to all 222 | subsequent copies and derivative works made from that copy. 223 | 224 | This option is useful when you wish to copy part of the code of 225 | the Library into a program that is not a library. 226 | 227 | 4. You may copy and distribute the Library (or a portion or 228 | derivative of it, under Section 2) in object code or executable form 229 | under the terms of Sections 1 and 2 above provided that you accompany 230 | it with the complete corresponding machine-readable source code, which 231 | must be distributed under the terms of Sections 1 and 2 above on a 232 | medium customarily used for software interchange. 233 | 234 | If distribution of object code is made by offering access to copy 235 | from a designated place, then offering equivalent access to copy the 236 | source code from the same place satisfies the requirement to 237 | distribute the source code, even though third parties are not 238 | compelled to copy the source along with the object code. 239 | 240 | 5. A program that contains no derivative of any portion of the 241 | Library, but is designed to work with the Library by being compiled or 242 | linked with it, is called a "work that uses the Library". Such a 243 | work, in isolation, is not a derivative work of the Library, and 244 | therefore falls outside the scope of this License. 245 | 246 | However, linking a "work that uses the Library" with the Library 247 | creates an executable that is a derivative of the Library (because it 248 | contains portions of the Library), rather than a "work that uses the 249 | library". The executable is therefore covered by this License. 250 | Section 6 states terms for distribution of such executables. 251 | 252 | When a "work that uses the Library" uses material from a header file 253 | that is part of the Library, the object code for the work may be a 254 | derivative work of the Library even though the source code is not. 255 | Whether this is true is especially significant if the work can be 256 | linked without the Library, or if the work is itself a library. The 257 | threshold for this to be true is not precisely defined by law. 258 | 259 | If such an object file uses only numerical parameters, data 260 | structure layouts and accessors, and small macros and small inline 261 | functions (ten lines or less in length), then the use of the object 262 | file is unrestricted, regardless of whether it is legally a derivative 263 | work. (Executables containing this object code plus portions of the 264 | Library will still fall under Section 6.) 265 | 266 | Otherwise, if the work is a derivative of the Library, you may 267 | distribute the object code for the work under the terms of Section 6. 268 | Any executables containing that work also fall under Section 6, 269 | whether or not they are linked directly with the Library itself. 270 | 271 | 6. As an exception to the Sections above, you may also combine or 272 | link a "work that uses the Library" with the Library to produce a 273 | work containing portions of the Library, and distribute that work 274 | under terms of your choice, provided that the terms permit 275 | modification of the work for the customer's own use and reverse 276 | engineering for debugging such modifications. 277 | 278 | You must give prominent notice with each copy of the work that the 279 | Library is used in it and that the Library and its use are covered by 280 | this License. You must supply a copy of this License. If the work 281 | during execution displays copyright notices, you must include the 282 | copyright notice for the Library among them, as well as a reference 283 | directing the user to the copy of this License. Also, you must do one 284 | of these things: 285 | 286 | a) Accompany the work with the complete corresponding 287 | machine-readable source code for the Library including whatever 288 | changes were used in the work (which must be distributed under 289 | Sections 1 and 2 above); and, if the work is an executable linked 290 | with the Library, with the complete machine-readable "work that 291 | uses the Library", as object code and/or source code, so that the 292 | user can modify the Library and then relink to produce a modified 293 | executable containing the modified Library. (It is understood 294 | that the user who changes the contents of definitions files in the 295 | Library will not necessarily be able to recompile the application 296 | to use the modified definitions.) 297 | 298 | b) Use a suitable shared library mechanism for linking with the 299 | Library. A suitable mechanism is one that (1) uses at run time a 300 | copy of the library already present on the user's computer system, 301 | rather than copying library functions into the executable, and (2) 302 | will operate properly with a modified version of the library, if 303 | the user installs one, as long as the modified version is 304 | interface-compatible with the version that the work was made with. 305 | 306 | c) Accompany the work with a written offer, valid for at 307 | least three years, to give the same user the materials 308 | specified in Subsection 6a, above, for a charge no more 309 | than the cost of performing this distribution. 310 | 311 | d) If distribution of the work is made by offering access to copy 312 | from a designated place, offer equivalent access to copy the above 313 | specified materials from the same place. 314 | 315 | e) Verify that the user has already received a copy of these 316 | materials or that you have already sent this user a copy. 317 | 318 | For an executable, the required form of the "work that uses the 319 | Library" must include any data and utility programs needed for 320 | reproducing the executable from it. However, as a special exception, 321 | the materials to be distributed need not include anything that is 322 | normally distributed (in either source or binary form) with the major 323 | components (compiler, kernel, and so on) of the operating system on 324 | which the executable runs, unless that component itself accompanies 325 | the executable. 326 | 327 | It may happen that this requirement contradicts the license 328 | restrictions of other proprietary libraries that do not normally 329 | accompany the operating system. Such a contradiction means you cannot 330 | use both them and the Library together in an executable that you 331 | distribute. 332 | 333 | 7. You may place library facilities that are a work based on the 334 | Library side-by-side in a single library together with other library 335 | facilities not covered by this License, and distribute such a combined 336 | library, provided that the separate distribution of the work based on 337 | the Library and of the other library facilities is otherwise 338 | permitted, and provided that you do these two things: 339 | 340 | a) Accompany the combined library with a copy of the same work 341 | based on the Library, uncombined with any other library 342 | facilities. This must be distributed under the terms of the 343 | Sections above. 344 | 345 | b) Give prominent notice with the combined library of the fact 346 | that part of it is a work based on the Library, and explaining 347 | where to find the accompanying uncombined form of the same work. 348 | 349 | 8. You may not copy, modify, sublicense, link with, or distribute 350 | the Library except as expressly provided under this License. Any 351 | attempt otherwise to copy, modify, sublicense, link with, or 352 | distribute the Library is void, and will automatically terminate your 353 | rights under this License. However, parties who have received copies, 354 | or rights, from you under this License will not have their licenses 355 | terminated so long as such parties remain in full compliance. 356 | 357 | 9. You are not required to accept this License, since you have not 358 | signed it. However, nothing else grants you permission to modify or 359 | distribute the Library or its derivative works. These actions are 360 | prohibited by law if you do not accept this License. Therefore, by 361 | modifying or distributing the Library (or any work based on the 362 | Library), you indicate your acceptance of this License to do so, and 363 | all its terms and conditions for copying, distributing or modifying 364 | the Library or works based on it. 365 | 366 | 10. Each time you redistribute the Library (or any work based on the 367 | Library), the recipient automatically receives a license from the 368 | original licensor to copy, distribute, link with or modify the Library 369 | subject to these terms and conditions. You may not impose any further 370 | restrictions on the recipients' exercise of the rights granted herein. 371 | You are not responsible for enforcing compliance by third parties with 372 | this License. 373 | 374 | 11. If, as a consequence of a court judgment or allegation of patent 375 | infringement or for any other reason (not limited to patent issues), 376 | conditions are imposed on you (whether by court order, agreement or 377 | otherwise) that contradict the conditions of this License, they do not 378 | excuse you from the conditions of this License. If you cannot 379 | distribute so as to satisfy simultaneously your obligations under this 380 | License and any other pertinent obligations, then as a consequence you 381 | may not distribute the Library at all. For example, if a patent 382 | license would not permit royalty-free redistribution of the Library by 383 | all those who receive copies directly or indirectly through you, then 384 | the only way you could satisfy both it and this License would be to 385 | refrain entirely from distribution of the Library. 386 | 387 | If any portion of this section is held invalid or unenforceable under any 388 | particular circumstance, the balance of the section is intended to apply, 389 | and the section as a whole is intended to apply in other circumstances. 390 | 391 | It is not the purpose of this section to induce you to infringe any 392 | patents or other property right claims or to contest validity of any 393 | such claims; this section has the sole purpose of protecting the 394 | integrity of the free software distribution system which is 395 | implemented by public license practices. Many people have made 396 | generous contributions to the wide range of software distributed 397 | through that system in reliance on consistent application of that 398 | system; it is up to the author/donor to decide if he or she is willing 399 | to distribute software through any other system and a licensee cannot 400 | impose that choice. 401 | 402 | This section is intended to make thoroughly clear what is believed to 403 | be a consequence of the rest of this License. 404 | 405 | 12. If the distribution and/or use of the Library is restricted in 406 | certain countries either by patents or by copyrighted interfaces, the 407 | original copyright holder who places the Library under this License may add 408 | an explicit geographical distribution limitation excluding those countries, 409 | so that distribution is permitted only in or among countries not thus 410 | excluded. In such case, this License incorporates the limitation as if 411 | written in the body of this License. 412 | 413 | 13. The Free Software Foundation may publish revised and/or new 414 | versions of the Lesser General Public License from time to time. 415 | Such new versions will be similar in spirit to the present version, 416 | but may differ in detail to address new problems or concerns. 417 | 418 | Each version is given a distinguishing version number. If the Library 419 | specifies a version number of this License which applies to it and 420 | "any later version", you have the option of following the terms and 421 | conditions either of that version or of any later version published by 422 | the Free Software Foundation. If the Library does not specify a 423 | license version number, you may choose any version ever published by 424 | the Free Software Foundation. 425 | 426 | 14. If you wish to incorporate parts of the Library into other free 427 | programs whose distribution conditions are incompatible with these, 428 | write to the author to ask for permission. For software which is 429 | copyrighted by the Free Software Foundation, write to the Free 430 | Software Foundation; we sometimes make exceptions for this. Our 431 | decision will be guided by the two goals of preserving the free status 432 | of all derivatives of our free software and of promoting the sharing 433 | and reuse of software generally. 434 | 435 | NO WARRANTY 436 | 437 | 15. BECAUSE THE LIBRARY IS LICENSED FREE OF CHARGE, THERE IS NO 438 | WARRANTY FOR THE LIBRARY, TO THE EXTENT PERMITTED BY APPLICABLE LAW. 439 | EXCEPT WHEN OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR 440 | OTHER PARTIES PROVIDE THE LIBRARY "AS IS" WITHOUT WARRANTY OF ANY 441 | KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE 442 | IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR 443 | PURPOSE. THE ENTIRE RISK AS TO THE QUALITY AND PERFORMANCE OF THE 444 | LIBRARY IS WITH YOU. SHOULD THE LIBRARY PROVE DEFECTIVE, YOU ASSUME 445 | THE COST OF ALL NECESSARY SERVICING, REPAIR OR CORRECTION. 446 | 447 | 16. IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN 448 | WRITING WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MAY MODIFY 449 | AND/OR REDISTRIBUTE THE LIBRARY AS PERMITTED ABOVE, BE LIABLE TO YOU 450 | FOR DAMAGES, INCLUDING ANY GENERAL, SPECIAL, INCIDENTAL OR 451 | CONSEQUENTIAL DAMAGES ARISING OUT OF THE USE OR INABILITY TO USE THE 452 | LIBRARY (INCLUDING BUT NOT LIMITED TO LOSS OF DATA OR DATA BEING 453 | RENDERED INACCURATE OR LOSSES SUSTAINED BY YOU OR THIRD PARTIES OR A 454 | FAILURE OF THE LIBRARY TO OPERATE WITH ANY OTHER SOFTWARE), EVEN IF 455 | SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH 456 | DAMAGES. 457 | 458 | END OF TERMS AND CONDITIONS 459 | 460 | How to Apply These Terms to Your New Libraries 461 | 462 | If you develop a new library, and you want it to be of the greatest 463 | possible use to the public, we recommend making it free software that 464 | everyone can redistribute and change. You can do so by permitting 465 | redistribution under these terms (or, alternatively, under the terms of the 466 | ordinary General Public License). 467 | 468 | To apply these terms, attach the following notices to the library. It is 469 | safest to attach them to the start of each source file to most effectively 470 | convey the exclusion of warranty; and each file should have at least the 471 | "copyright" line and a pointer to where the full notice is found. 472 | 473 | 474 | Copyright (C) 475 | 476 | This library is free software; you can redistribute it and/or 477 | modify it under the terms of the GNU Lesser General Public 478 | License as published by the Free Software Foundation; either 479 | version 2.1 of the License, or (at your option) any later version. 480 | 481 | This library is distributed in the hope that it will be useful, 482 | but WITHOUT ANY WARRANTY; without even the implied warranty of 483 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU 484 | Lesser General Public License for more details. 485 | 486 | You should have received a copy of the GNU Lesser General Public 487 | License along with this library; if not, write to the Free Software 488 | Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 489 | USA 490 | 491 | Also add information on how to contact you by electronic and paper mail. 492 | 493 | You should also get your employer (if you work as a programmer) or your 494 | school, if any, to sign a "copyright disclaimer" for the library, if 495 | necessary. Here is a sample; alter the names: 496 | 497 | Yoyodyne, Inc., hereby disclaims all copyright interest in the 498 | library `Frob' (a library for tweaking knobs) written by James Random 499 | Hacker. 500 | 501 | , 1 April 1990 502 | Ty Coon, President of Vice 503 | 504 | That's all there is to it! -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # ManagedC 2 | 3 | Library to add a reference counter to C. See [`tests/src/`](tests/src) for examples 4 | 5 | > |ℹ️ Note ℹ️ | 6 | > |-----------------| 7 | > | If you have GCC extensions enabled, you get access to more features, i.e the `auto` macro for automatic releasing! | 8 | 9 | 10 | # Installation 11 | 12 | Using [xmake](https://xmake.io) you can easily use this library by adding `managedc` to your packages 13 | 14 | If you do not use `xmake` then you can just copy the file [`managed.h`](src/managed.h) from [`src/`](src/). [`mstring.h`](src/managed/mstring.h) adds helper functions for string usage. 15 | 16 | # Usage 17 | 18 | ## Allocation and deallocation 19 | 20 | To get a "managed" type, you must allocate your type with 21 | ```c 22 | static void *mc_nullable managed_allocate(size_t count, size_t typesize, void (*mc_nullable)(void *mc_nonnull) free, const void *mc_nullable data) 23 | ```` 24 | or 25 | ```c 26 | #define mc_alloc(T, free) (T *)managed_allocate(1, sizeof(T), (managed_Free_f *)free, NULL) 27 | ``` 28 | or, for arrays 29 | ```c 30 | #define mc_array(T, count, free) (T *)managed_allocate(count, sizeof(T), (managed_Free_f *)(free), NULL) 31 | ``` 32 | 33 | and you can release your reference using 34 | ```c 35 | static void managed_release(const void *mc_nonnull ptr) 36 | ``` 37 | or 38 | ```c 39 | #define mc_free(ptr) managed_release(ptr) 40 | ``` 41 | 42 | You can specify a custom "deconstructor", which is a function that will run on each element of the array on deallocation 43 | 44 | ```c 45 | #include 46 | 47 | #include "managed.h" 48 | 49 | //We get a pointer to the value that we can free 50 | //It is a double pointer because we have an array 51 | //of pointers in main 52 | void free_int_ref(int **ref) 53 | { 54 | //This function will be called for each item in the array 55 | printf("Value: %d\n", **ref); 56 | 57 | //mc_free is used for deallocating "managed" types. 58 | mc_free(ref); 59 | } 60 | 61 | int main(void) 62 | { 63 | int **alloc_list = mc_array(int *, 5, &free_int_ref); 64 | 65 | alloc_list[0] = mc_alloc(int, NULL); 66 | *alloc_list[0] = 11; 67 | 68 | alloc_list[1] = mc_alloc(int, NULL); 69 | *alloc_list[1] = 10; 70 | 71 | alloc_list[2] = mc_alloc(int, NULL); 72 | *alloc_list[2] = 9; 73 | 74 | alloc_list[3] = mc_alloc(int, NULL); 75 | *alloc_list[3] = 8; 76 | 77 | alloc_list[4] = mc_alloc(int, NULL); 78 | *alloc_list[4] = 7; 79 | 80 | mc_free(alloc_list); 81 | } 82 | ``` 83 | 84 | ## Referencing 85 | 86 | For the reference counter to work, assigning a reference through the regular `=` will result in no additional references being counted, which is useful for "weak" references. To make a strong reference to a "managed" pointer, use 87 | ```c 88 | static void *managed_reference(const void *mc_nonnull ptr) 89 | ``` 90 | or the 91 | ```c 92 | #define mc_ref(ptr) MC_EXPAND((mc_typeof(ptr)))managed_reference(ptr) 93 | ``` 94 | macro. 95 | 96 | The pointer will not be freed until all the references are not in use. 97 | 98 | ```c 99 | #include "managed.h" 100 | 101 | int *func_that_allocs(void) 102 | { 103 | int *mem = managed_alloc(sizeof(int), 128, NULL, NULL); 104 | 105 | return mem; 106 | } 107 | 108 | int main(void) 109 | { 110 | //You can pass in managed_release (not mc_free) as a param, and it will run on the value of the pointer, which must also be a managed pointer 111 | int **refarray = mc_alloc(int *, managed_release); 112 | 113 | { 114 | int *array = func_that_allocs(); 115 | *array = 10; 116 | refarray[0] = mc_reference(array); //Will not be deallocateed because we got a reference 117 | mc_free(array); 118 | } 119 | 120 | mc_free(refarray); 121 | } 122 | ``` 123 | 124 | ## Metadata 125 | 126 | ManagedC keeps metadata about the pointer, meaning that you do not have to keep track of this data yourself. 127 | The metadata is stored in this struct 128 | ```c 129 | struct managed_PointerInfo { 130 | /** 131 | * count: Number of used array elements. 132 | * capacity: Number of allocated array elements. (Used for managed_Vector) 133 | * typesize: Size of the type. 134 | * reference_count: Number of references to this pointer. 135 | */ 136 | size_t count, capacity, typesize, reference_count; 137 | 138 | /** 139 | * Function to call on 0 references. 140 | */ 141 | managed_Free_f *mc_nullable free; 142 | 143 | /** 144 | * Pointer to the data, should be just in front of data. 145 | */ 146 | void *mc_nonnull data; 147 | 148 | #if MC_MUTEX 149 | /** 150 | * Lock on the metadata, maps to an OS specific object. 151 | */ 152 | mc_Mutex_t lock; 153 | #endif 154 | }; 155 | ``` 156 | It can be accessed by using 157 | ```c 158 | const struct managed_PointerInfo *mc_nullable managed_info_of(const void *mc_nonnull ptr) 159 | ``` 160 | 161 | As `count` is the most common field you will probably access, you can call 162 | ```c 163 | #define mc_countof(ptr) (managed_info_of(ptr)->count) 164 | ``` 165 | to quickly get the count of elements in the allocations. 166 | 167 | ### Extensions - GCC/Clang 168 | 169 | Just as the original use of `auto` in BCPL was to declare that a stack-allocated variable was to be deallocated once outside of the scope, `mc_auto` in this library declares that a **heap allocated variable allocated with `managed_allocate` will be freed once outside the scope**. 170 | 171 | For example 172 | 173 | ```c 174 | #include "managed.h" 175 | 176 | void func_that_allocs(void) 177 | { 178 | mc_auto int *mem = mc_alloc(int, NULL); 179 | //...usage 180 | 181 | //Automatically deallocated! 182 | } 183 | 184 | int main(void) 185 | { 186 | func_that_allocs(); 187 | func_that_allocs(); 188 | func_that_allocs(); 189 | func_that_allocs(); 190 | func_that_allocs(); 191 | //no leaks! 192 | } 193 | ``` 194 | 195 | ### Extensions - Clang 196 | 197 | If you use clang, you can use `mc_defer` to make code run once out of scope. This requires `-fBlocks` and you may have to link with `libBlocksRuntime`. 198 | ```c 199 | int main(void) 200 | { 201 | FILE *fp = fopen("hi.txt", "rb"); 202 | mc_defer { fclose(fp); }; 203 | //...usage 204 | //Automatically closed! 205 | } 206 | ``` 207 | 208 | ## Datastructures 209 | 210 | ManagedC also comes with reference counted common data structures, with the list still growing 211 | You can see them [here](src/managed/) 212 | 213 | ### TODO 214 | - [x] Safer and better thread support - [issue](https://github.com/Frityet/ManagedC/issues/1) 215 | - [X] Fix for reallocation - [issue](https://github.com/Frityet/ManagedC/issues/3) 216 | - [X] A solution to circular references - [issue](https://github.com/Frityet/ManagedC/issues/2) 217 | - [X] Solution for MSVC/ISO-C - [issue](https://github.com/Frityet/ManagedC/issues/4) 218 | - [X] Easier/standardized referencing - [issue](https://github.com/Frityet/ManagedC/issues/5) 219 | - [X] Data structures - [issue](https://github.com/Frityet/ManagedC/issues/8) 220 | 221 | - [ ] Even more data structures! - [issue](https://github.com/Frityet/ManagedC/issues/10) 222 | -------------------------------------------------------------------------------- /package.lua: -------------------------------------------------------------------------------- 1 | package("managedc") 2 | set_kind("library", { headeronly = true }) 3 | set_description("Reference counter garabage collection for C") 4 | 5 | add_urls("https://github.com/Frityet/ManagedC.git") 6 | add_versions("1.0.0", "350800021f8d41e5717e76f8dfb0485d45781996") 7 | add_versions("1.1.0", "878158a7f185aaaa38b8855afd91f89b05c9df13") 8 | add_versions("1.1.1", "eb70de26ca4cc724efd14dc50ec9bc990ec92271") 9 | add_versions("1.1.2", "0068462307a1901a1b30410ae53721ca7e88cb10") 10 | add_versions("1.2.0", "65dadb723a557b1e295b2890e2eeac2c0e865880") 11 | add_versions("1.2.1", "6c46b8b9764cabf7cf9c0a49e6ee7aa35039f4c1") 12 | add_versions("1.3.0", "cc900aea02c39e6d5ee7dc38f1b14c3959eca008") 13 | add_versions("1.3.1", "0b14ab3c61682963c71613a73dad6aeb7dd446ff") 14 | add_versions("1.4.0", "d5e445d4d1aed726226342b15704cb64ffa667c6") 15 | add_versions("1.4.1", "19ace17f26460e2a2aa6969414b499c2a8f2964d") 16 | add_versions("1.4.2", "b12303b3d5d26097554efbd05930b47030fe4711") 17 | add_versions("1.5.0", "f4cce9c1aee952d603c18b73dc6219ea15b91717") 18 | add_versions("2.0.0-beta", "6963abbe67fccce834e4cc1c5d3ecdb73a55701f") 19 | add_versions("2.1.0-beta", "1217d7f55ee04f2b692e998e451b5a6b000f4618") 20 | add_versions("2.2.1-beta", "ca8f6af418e3d4713cb902128232c15e2dafb071") 21 | add_versions("2.3.0", "6a4f91f2b839fc26995008b42bcefe3a4b54ef27") 22 | add_versions("2.3.1", "44725f403aca7234c2a662315fa43ce5f9fb2ff6") 23 | 24 | on_install(function (package) 25 | os.cp("**.h", package:installdir("include"), { 26 | rootdir = "src/" 27 | }) 28 | end) 29 | 30 | on_test(function (package) 31 | assert(package:has_cfuncs("managed_allocate", { includes = "managed.h" })) 32 | assert(package:has_cfuncs("managed_string", { includes = "managed/mstring.h" })) 33 | end) 34 | -------------------------------------------------------------------------------- /src/managed.h: -------------------------------------------------------------------------------- 1 | /** 2 | * Copyright (C) 2023 Amrit Bhogal 3 | * 4 | * This file is part of ManagedC. 5 | * 6 | * ManagedC is free software: you can redistribute it and/or modify 7 | * it under the terms of the GNU General Public License as published by 8 | * the Free Software Foundation, either version 3 of the License, or 9 | * (at your option) any later version. 10 | * 11 | * ManagedC is distributed in the hope that it will be useful, 12 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 13 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 14 | * GNU General Public License for more details. 15 | * 16 | * You should have received a copy of the GNU General Public License 17 | * along with ManagedC. If not, see . 18 | */ 19 | 20 | #if !defined(MANAGEDC_MAIN) 21 | #define MANAGEDC_MAIN 22 | 23 | #include 24 | #include 25 | #include 26 | #include 27 | 28 | #if defined (__unix__) || (defined (__APPLE__) && defined (__MACH__)) || defined (__linux__) || defined (MC_POSIX) 29 | # define MC_POSIX 1 30 | # include 31 | # include 32 | #else 33 | # define MC_POSIX 0 34 | #endif 35 | 36 | #if defined(WIN32) || defined(MC_WIN32) 37 | # define MC_WIN32 1 38 | # include 39 | #else 40 | # define MC_WIN32 0 41 | #endif 42 | 43 | #if !defined(MC_NO_THREAD_SAFTEY) 44 | 45 | # if MC_POSIX 46 | typedef pthread_mutex_t mc_Mutex_t; 47 | # define MC_MUTEX 1 48 | # elif defined(MC_WIN32) 49 | typedef HANDLE mc_mutex_t; 50 | # define MC_MUTEX 1 51 | # else 52 | # pragma warning Mutex support disabled (unrecognized platform) 53 | # define MC_MUTEX 0 54 | # endif 55 | #else 56 | # define MC_MUTEX 0 57 | #endif 58 | 59 | #if !defined(MC_UINTPTR) 60 | typedef unsigned long int mc_uintptr_t; 61 | #endif 62 | 63 | #if !defined(MC_GUARDPAGE_MAX) 64 | # define MC_GUARDPAGE_MAX ((uintptr_t)0x1000) 65 | #endif 66 | 67 | #if !defined(MC_ALLOCATOR) 68 | # define MC_ALLOCATOR(c, s) calloc(c, s) 69 | #endif 70 | 71 | #if !defined(MC_MEMCPY) 72 | # define MC_MEMCPY(dst, src, nmemb) memcpy(dst, src, nmemb) 73 | #endif 74 | 75 | #if !defined(MC_MEMMOVE) 76 | # define MC_MEMMOVE(dst, src, nmemb) memmove(dst, src, nmemb) 77 | #endif 78 | 79 | #if !defined(MC_FREE) 80 | # define MC_FREE(ptr) free(ptr) 81 | #endif 82 | 83 | #if !defined(__GNUC__) || defined(__STRICT_ANSI__) 84 | # define MC_ANSI 1 85 | #else 86 | # define MC_ANSI 0 87 | #endif 88 | 89 | #if defined(__clang__) && defined(__llvm__) && !MC_ANSI 90 | # define MC_LLVM 1 91 | #else 92 | # define MC_LLVM 0 93 | #endif 94 | 95 | #if MC_ANSI 96 | # define mc_nullable 97 | # define mc_nonnull 98 | # define mc_attribute(t) 99 | # define mc_defer 100 | # define mc_typeof(T) 101 | # define MC_EXPAND(t) 102 | # define mc_pragma(p) 103 | # define mc_warning(w) 104 | #else 105 | # define MC_EXPAND(...) __VA_ARGS__ 106 | # define MC_concat2(x) _mc_##x##_deferexpr 107 | # define MC_concat1(x) MC_concat2(x) 108 | 109 | # define mc_typeof(...) __typeof__(__VA_ARGS__) 110 | # define mc_attribute(...) __attribute__((__VA_ARGS__)) 111 | # define mc_pragma(...) _Pragma(#__VA_ARGS__) 112 | # define mc_warning(...) mc_pragma(GCC warning "\"" __VA_ARGS__ "\"") 113 | 114 | # if MC_LLVM 115 | # define mc_nullable _Nullable 116 | # define mc_nonnull _Nonnull 117 | # define mc_nocapture __block 118 | # define mc_defer mc_attribute(cleanup(_mc_rundefer)) void (^mc_nonnull MC_concat1(__LINE__))(void) = ^ 119 | static void _mc_rundefer(void (^mc_nonnull *mc_nonnull cb)(void)) { (*cb)(); } 120 | 121 | # else 122 | # define mc_nullable 123 | # define mc_nonnull 124 | # define mc_defer 125 | # define mc_nocapture 126 | # endif 127 | #endif 128 | 129 | #if MC_ANSI 130 | # define mc_auto "Running in ANSI standard mode (no extensions). This macro does not automatically release the pointer!" 131 | #else 132 | static void managed_release(void *mc_nonnull ptr); 133 | static void managed_release_ptr(void *mc_nonnull addr) 134 | { 135 | void **ptr = addr; 136 | if (ptr != NULL && *ptr != NULL) 137 | { 138 | managed_release(*ptr); 139 | *ptr = NULL; 140 | } 141 | } 142 | 143 | # define mc_auto mc_attribute(cleanup(managed_release_ptr)) 144 | #endif 145 | 146 | #if MC_MUTEX 147 | static int mc_mutex_create(mc_Mutex_t *mc_nonnull mtx) 148 | { 149 | #if defined(MC_POSIX) 150 | return pthread_mutex_init(mtx, NULL) == 0 ? 0 : 1; 151 | # elif defined(MC_WIN32) 152 | *mtx = CreateMutex(NULL, 0, NULL); 153 | return 0; 154 | #endif 155 | } 156 | 157 | static int mc_mutex_destroy(mc_Mutex_t *mc_nonnull mtx) 158 | { 159 | #if defined(MC_POSIX) 160 | return pthread_mutex_destroy(mtx); 161 | # elif defined(MC_WIN32) 162 | CloseHandle(*mtx); 163 | return 0; 164 | #endif 165 | } 166 | 167 | static int mc_mutex_lock(mc_Mutex_t *mc_nonnull mtx) 168 | { 169 | #if defined(MC_POSIX) 170 | return pthread_mutex_lock(mtx) == 0 ? 0 : 1; 171 | # elif defined(MC_WIN32) 172 | return WaitForSingleObject(*mtx, INFINITE) == WAIT_OBJECT_0 ? 0 : 1; 173 | #endif 174 | } 175 | 176 | static int mc_mutex_unlock(mc_Mutex_t *mc_nonnull mtx) 177 | { 178 | #if defined(MC_POSIX) 179 | return pthread_mutex_unlock(mtx) == 0 ? 0 : 1; 180 | # elif defined(MC_WIN32) 181 | return ReleaseMutex(*mtx) == 0 ? 1 : 0; /*Fuck MS*/ 182 | #endif 183 | } 184 | 185 | # define MC_MUTEX_CREATE(mtx) mc_mutex_create(mtx) 186 | # define MC_MUTEX_DESTROY(mtx) mc_mutex_destroy(mtx) 187 | # define MC_MUTEX_LOCK(mtx) mc_mutex_lock(mtx) 188 | # define MC_MUTEX_UNLOCK(mtx) mc_mutex_unlock(mtx) 189 | 190 | #else 191 | # define MC_MUTEX_CREATE(mtx) 192 | # define MC_MUTEX_DESTROY(mtx) 193 | # define MC_MUTEX_LOCK(mtx) 194 | # define MC_MUTEX_UNLOCK(mtx) 195 | #endif 196 | 197 | #define _mcinternal_ptrinfo(ptr) ((struct managed_PointerInfo *)managed_info_of(ptr)) 198 | 199 | typedef void managed_Free_f(void *mc_nonnull alloc); 200 | 201 | struct managed_PointerInfo { 202 | /** 203 | * count: Number of used array elements. 204 | * capacity: Number of allocated array elements. (Used for managed_Vector) 205 | * typesize: Size of the type. 206 | * reference_count: Number of references to this pointer. 207 | */ 208 | size_t count, capacity, typesize, reference_count; 209 | 210 | /** 211 | * Function to call on 0 references. 212 | */ 213 | managed_Free_f *mc_nullable free; 214 | 215 | /** 216 | * Pointer to the data, should be just in front of data. 217 | */ 218 | void *mc_nonnull data; 219 | 220 | #if MC_MUTEX 221 | /** 222 | * Lock on the metadata, maps to an OS specific object. 223 | */ 224 | mc_Mutex_t lock; 225 | #endif 226 | }; 227 | 228 | /** 229 | * Gets the metadata about the pointer 230 | */ 231 | static const struct managed_PointerInfo *mc_nullable managed_info_of(const void *mc_nonnull ptr) 232 | { 233 | struct managed_PointerInfo *info = NULL; 234 | 235 | /*Check if the pointer is lesser than the max page, which *probably* means that it is not on the heap*/ 236 | if ((uintptr_t)ptr < MC_GUARDPAGE_MAX) return NULL; 237 | info = (struct managed_PointerInfo *)ptr - 1; 238 | if (info->data != ptr) 239 | return NULL; 240 | 241 | return info; 242 | } 243 | 244 | static long int mc_countof(const void *mc_nonnull ptr) 245 | { 246 | const struct managed_PointerInfo *info = managed_info_of(ptr); 247 | size_t count = 0; 248 | if (info == NULL) return -1; 249 | if (MC_MUTEX_LOCK((mc_Mutex_t *)&info->lock) != 0) return -2; 250 | count = info->count; 251 | if (MC_MUTEX_UNLOCK((mc_Mutex_t *)&info->lock) != 0) return -3; 252 | return (long int)count; 253 | } 254 | 255 | static long int mc_sizeof_type(const void *mc_nonnull ptr) 256 | { 257 | const struct managed_PointerInfo *info = managed_info_of(ptr); 258 | size_t size = 0; 259 | if (info == NULL) return -1; 260 | if (MC_MUTEX_LOCK((mc_Mutex_t *)&info->lock) != 0) return -2; 261 | size = info->typesize; 262 | if (MC_MUTEX_UNLOCK((mc_Mutex_t *)&info->lock) != 0) return -3; 263 | return (long int)size; 264 | } 265 | 266 | static long int mc_sizeof(const void *mc_nonnull ptr) 267 | { 268 | long int c = mc_countof(ptr), tsiz = mc_sizeof_type(ptr); 269 | if (c < 0 || tsiz < 0) return -1; /*TODO: do the math to make this work properly*/ 270 | return c * tsiz; 271 | } 272 | 273 | #define mc_alloc(T, free) (T *)managed_allocate(1, sizeof(T), (managed_Free_f *)(free), NULL) 274 | #define mc_array(T, count, free) (T *)managed_allocate(count, sizeof(T), (managed_Free_f *)(free), NULL) 275 | 276 | /** 277 | * Creates a new allocation with the managed pointer metadata. Define MC_ALLOCATOR(c, nmemb) to change the allocation function 278 | */ 279 | static void *mc_nullable managed_allocate(size_t count, size_t typesize, managed_Free_f *mc_nullable free, const void *mc_nullable data) 280 | { 281 | struct managed_PointerInfo *info = MC_ALLOCATOR(1, sizeof(struct managed_PointerInfo) + count * typesize); 282 | if (info == NULL) return NULL; 283 | 284 | info->capacity = info->count= count; 285 | info->typesize = typesize; 286 | info->free = free; 287 | info->reference_count = 1; 288 | /* The data must be right after the metadata */ 289 | info->data = info + 1; 290 | if (MC_MUTEX_CREATE(&info->lock) != 0) { 291 | MC_FREE(info); 292 | return NULL; 293 | } 294 | 295 | if (data != NULL) 296 | MC_MEMCPY(info->data, data, count * typesize); 297 | 298 | return info->data; 299 | } 300 | 301 | 302 | /** 303 | * Creates a copy of the managed pointer. This copies every byte of data in the allocation 304 | */ 305 | static void *mc_nullable managed_copy(const void *mc_nonnull ptr, long int count) 306 | { 307 | struct managed_PointerInfo *info = (void *)managed_info_of(ptr); 308 | void *alloc = NULL; 309 | if (info == NULL) return NULL; 310 | if (MC_MUTEX_LOCK(&info->lock) != 0) return NULL; 311 | 312 | if (count < 1) count = (long int)info->count; 313 | alloc = managed_allocate((size_t)count, info->typesize, info->free,NULL); 314 | if (alloc == NULL) return NULL; 315 | 316 | /* Just in case count is larger than mc_countof(ptr) (sizing up an allocation), make sure you only copy the existing data */ 317 | MC_MEMCPY(alloc, ptr, (size_t)(info->typesize * info->count)); 318 | MC_MUTEX_UNLOCK(&info->lock); /*TODO: what should I even do if this fails?*/ 319 | return alloc; 320 | } 321 | 322 | static void *mc_nullable mc_dup(const void *mc_nonnull ptr) 323 | { return managed_copy(ptr, mc_countof(ptr)); } 324 | 325 | #define mc_ref(ptr) MC_EXPAND((mc_typeof(ptr)))managed_reference(ptr) 326 | /** 327 | * Gets a reference to the ptr, and incrememnts it's reference count 328 | */ 329 | static void *mc_nullable managed_reference(void *mc_nonnull ptr) 330 | { 331 | struct managed_PointerInfo *info = (void *)managed_info_of(ptr); 332 | if (info == NULL) return NULL; 333 | if (MC_MUTEX_LOCK(&info->lock) != 0) return NULL; 334 | info->reference_count++; 335 | if (MC_MUTEX_UNLOCK(&info->lock) != 0) return NULL; 336 | return ptr; 337 | } 338 | 339 | /* 340 | * Releases a reference to the pointer, and if 0 references, frees the pointer 341 | */ 342 | static void managed_release(void *mc_nonnull ptr) 343 | { 344 | struct managed_PointerInfo *info = NULL; 345 | size_t i = 0; 346 | 347 | info = (struct managed_PointerInfo *)managed_info_of(ptr); 348 | if (info == NULL) return; 349 | 350 | if (MC_MUTEX_LOCK(&info->lock) != 0) return; 351 | info->reference_count--; 352 | if (info->reference_count < 1) { 353 | if (info->free != NULL) 354 | for (i = 0; i < info->count; i++) /* Free each item of the allocation individually */ { 355 | info->free(((unsigned char *)ptr) + i * info->typesize); 356 | } 357 | 358 | /* Unlock before freeing! */ 359 | if (!MC_MUTEX_UNLOCK(&info->lock)) return;/*TODO: Maybe not the best?*/ 360 | MC_MUTEX_DESTROY(&info->lock); 361 | MC_FREE(info); 362 | } 363 | MC_MUTEX_UNLOCK(&info->lock); 364 | } 365 | 366 | /** 367 | * The compiler complains if void *const * is passed to void *, so use this hack 368 | */ 369 | static void mc_free(const void *mc_nonnull ptr) 370 | { managed_release((void *)ptr); } 371 | 372 | /** 373 | * Creates a new, non-managed, allocation and copies all the data to it 374 | */ 375 | static void *mc_nullable managed_to_unmanaged(const void *mc_nonnull ptr) 376 | { 377 | struct managed_PointerInfo *info = _mcinternal_ptrinfo(ptr); 378 | void *unmanaged = NULL; 379 | if (info == NULL) return NULL; 380 | if (MC_MUTEX_LOCK(&info->lock) != 0) return NULL; 381 | unmanaged = MC_ALLOCATOR(info->count + 1, info->typesize); /* +1 just in case it's a string */ 382 | if (unmanaged == NULL) return NULL; 383 | 384 | MC_MEMCPY(unmanaged, ptr, info->count * info->typesize); 385 | 386 | /* welp, too bad I guess if it fails! */ 387 | MC_MUTEX_UNLOCK(&info->lock); 388 | return unmanaged; 389 | } 390 | 391 | #undef _mcinternal_ptrinfo 392 | #undef MC_SOURCE_PRIVATE 393 | 394 | #endif 395 | -------------------------------------------------------------------------------- /src/managed/marray.h: -------------------------------------------------------------------------------- 1 | /** 2 | * Copyright (C) 2023 Amrit Bhogal 3 | * 4 | * This file is part of ManagedC. 5 | * 6 | * ManagedC is free software: you can redistribute it and/or modify 7 | * it under the terms of the GNU General Public License as published by 8 | * the Free Software Foundation, either version 3 of the License, or 9 | * (at your option) any later version. 10 | * 11 | * ManagedC is distributed in the hope that it will be useful, 12 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 13 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 14 | * GNU General Public License for more details. 15 | * 16 | * You should have received a copy of the GNU General Public License 17 | * along with ManagedC. If not, see . 18 | */ 19 | 20 | #if !defined(MANAGEDC_MARRAY) 21 | #define MANAGEDC_MARRAY 22 | 23 | #include "managed.h" 24 | 25 | #if MC_ANSI 26 | # error "Managed array requires C99 + Extensions!" 27 | #endif 28 | 29 | #undef mc_array 30 | #define mc_array(free, ...) (mc_typeof((__VA_ARGS__)[0]) (*)[(sizeof(__VA_ARGS__) / sizeof((__VA_ARGS__)[0]))])managed_allocate(sizeof((__VA_ARGS__)[0]), (sizeof(__VA_ARGS__) / sizeof((__VA_ARGS__)[0])), free, __VA_ARGS__) 31 | 32 | 33 | #endif 34 | -------------------------------------------------------------------------------- /src/managed/mhashmap.h: -------------------------------------------------------------------------------- 1 | /** 2 | * Copyright (C) 2023 Amrit Bhogal 3 | * 4 | * This file is part of ManagedC. 5 | * 6 | * ManagedC is free software: you can redistribute it and/or modify 7 | * it under the terms of the GNU General Public License as published by 8 | * the Free Software Foundation, either version 3 of the License, or 9 | * (at your option) any later version. 10 | * 11 | * ManagedC is distributed in the hope that it will be useful, 12 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 13 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 14 | * GNU General Public License for more details. 15 | * 16 | * You should have received a copy of the GNU General Public License 17 | * along with ManagedC. If not, see . 18 | */ 19 | 20 | #if !defined(MANAGEDC_MHASHMAP) 21 | #define MANAGEDC_MHASHMAP 22 | 23 | #include "managed.h" 24 | 25 | #if !defined(MC_HASH) 26 | static unsigned long int managed_hash(unsigned char *data, size_t size) 27 | { 28 | unsigned long int hash = 0; 29 | size_t i = 0; 30 | 31 | for (i = 0; i < size; i++) 32 | hash = (hash << 5) - hash + data[i]; 33 | 34 | return hash; 35 | } 36 | # define MC_HASH(data, size) managed_hash((unsigned char *)data, (size_t)size) 37 | #endif 38 | 39 | #define mhashmap_t(K, V) struct { K *const key; V *const value; } 40 | 41 | static void *managed_hashmap(size_t keysize, size_t valsize, 42 | size_t count, managed_Free_f *keyfree, 43 | managed_Free_f *valfree, const void *data) 44 | { 45 | 46 | return NULL; 47 | } 48 | 49 | 50 | #endif 51 | -------------------------------------------------------------------------------- /src/managed/mlist.h: -------------------------------------------------------------------------------- 1 | /** 2 | * Copyright (C) 2023 Amrit Bhogal 3 | * 4 | * This file is part of ManagedC. 5 | * 6 | * ManagedC is free software: you can redistribute it and/or modify 7 | * it under the terms of the GNU General Public License as published by 8 | * the Free Software Foundation, either version 3 of the License, or 9 | * (at your option) any later version. 10 | * 11 | * ManagedC is distributed in the hope that it will be useful, 12 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 13 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 14 | * GNU General Public License for more details. 15 | * 16 | * You should have received a copy of the GNU General Public License 17 | * along with ManagedC. If not, see . 18 | */ 19 | 20 | #if !defined(MANAGEDC_MLIST) 21 | #define MANAGEDC_MLIST 22 | 23 | #include "managed.h" 24 | 25 | #define mlist_t(T) T *const 26 | 27 | #define _mcinternal_ptrinfo(ptr) ((struct managed_PointerInfo *)managed_info_of(ptr)) 28 | 29 | #if defined(MC_MUTEX) 30 | # define _mcinternal_lock(ptr) (MC_MUTEX_LOCK(&_mcinternal_ptrinfo(ptr)->lock)) 31 | # define _mcinternal_unlock(ptr) (MC_MUTEX_UNLOCK(&_mcinternal_ptrinfo(ptr)->lock)) 32 | #else 33 | # define _mcinternal_lock(ptr) 34 | # define _mcinternal_unlock(ptr) 35 | #endif 36 | 37 | #define MC_ASSERT_IS_MLIST(list) (({ mlist_t(mc_typeof(**list)) *_list_test_t_ = list; _list_test_t_; })) 38 | #define MC_ASSERT_DATA_TYPE(list, obj) (({ mc_typeof(**list) *_obj_test_t_ = (obj); (obj); })) 39 | 40 | static void managed_list_free(mlist_t(void) *list) 41 | { 42 | managed_release(*list); 43 | /* Because we set the metadata for the list beforehand, we must now reset it to 0 to avoid the free function being called in memory we do not own */ 44 | 45 | _mcinternal_ptrinfo(list)->count = 0; 46 | _mcinternal_ptrinfo(list)->capacity = 0; 47 | } 48 | 49 | #define mlist_new(type, free) (mlist_t(type) *)managed_list(sizeof(type), 2, free, NULL) 50 | static mlist_t(void) *managed_list(size_t typesize, size_t count, managed_Free_f *free, const void *data) 51 | { 52 | /* If we try and reallocate a pointer by itself, all existing references will break. */ 53 | /* To fix this for our list, we must allocate a pointer which will hold a pointer to the actual array */ 54 | void **list = managed_allocate(1, sizeof(void *), (managed_Free_f *)&managed_list_free, NULL); 55 | size_t cap = 0; 56 | if (list == NULL) return NULL; 57 | 58 | /* Make sure it's even */ 59 | cap = count + (count & 1); 60 | *list = managed_allocate(cap, typesize, free, NULL); 61 | if (*list == NULL) { 62 | mc_free(list); 63 | return NULL; 64 | } 65 | 66 | _mcinternal_lock(*list); 67 | _mcinternal_lock(list); 68 | if (data != NULL) 69 | MC_MEMCPY(*list, data, typesize * count); 70 | 71 | /* Do the same for the actual array */ 72 | _mcinternal_ptrinfo(*list)->count = 0; 73 | _mcinternal_ptrinfo(*list)->capacity = cap; 74 | 75 | /* So the semantics of mc_countof(list) work, we manually set the struct fields */ 76 | _mcinternal_ptrinfo(list)->count = 0; 77 | _mcinternal_ptrinfo(list)->capacity = cap; 78 | 79 | _mcinternal_unlock(list); 80 | _mcinternal_unlock(*list); 81 | 82 | return list; 83 | } 84 | 85 | #if MC_ANSI 86 | # define mlist_add(list, data) managed_list_add(list, data) 87 | #else 88 | # define mlist_add(list, data) managed_list_add(MC_ASSERT_IS_MLIST(list), MC_ASSERT_DATA_TYPE(list, data)) 89 | #endif 90 | static int managed_list_add(const void *ptr, const void *data) 91 | { /* The const in the arg is a complete lie, we must keep it or else the compiler complains though */ 92 | void **list = (void **)ptr; 93 | 94 | struct managed_PointerInfo *listinfo = _mcinternal_ptrinfo(list), *list_data_info = NULL, arrayinfo; 95 | if (listinfo == NULL) return 1; 96 | 97 | MC_MUTEX_LOCK(&listinfo->lock); 98 | 99 | /*We must copy the data because the allocation is freed later in the function*/ 100 | list_data_info = _mcinternal_ptrinfo(*list); 101 | if (list_data_info == NULL) return 2; 102 | arrayinfo = *list_data_info; 103 | 104 | 105 | if (arrayinfo.count >= arrayinfo.capacity) { 106 | /*1.5, being the closest to the golden ratio, is the most efficent cap size multiplier*/ 107 | /*Why? I have 0 fuckin clue*/ 108 | size_t newsiz = (size_t)((double)arrayinfo.capacity * 1.5); 109 | void *alloc = managed_allocate(newsiz, arrayinfo.typesize, arrayinfo.free, NULL); 110 | struct managed_PointerInfo *allocinfo = NULL; 111 | 112 | if (alloc == NULL) return 3; 113 | allocinfo = _mcinternal_ptrinfo(alloc); 114 | 115 | MC_MEMCPY(alloc, *list, arrayinfo.typesize * arrayinfo.count); 116 | allocinfo->count = arrayinfo.count; 117 | 118 | managed_release(*list); 119 | 120 | *list = alloc; 121 | } 122 | 123 | list_data_info = _mcinternal_ptrinfo(*list); 124 | if (list_data_info == NULL) return 2; 125 | 126 | MC_MUTEX_LOCK(&list_data_info->lock); 127 | MC_MEMCPY(((unsigned char *)*list) + arrayinfo.count * arrayinfo.typesize, data, arrayinfo.typesize); 128 | 129 | list_data_info->count++; 130 | listinfo->count = list_data_info->count; 131 | 132 | MC_MUTEX_UNLOCK(&list_data_info->lock); 133 | MC_MUTEX_UNLOCK(&listinfo->lock); 134 | return 0; 135 | } 136 | 137 | #if MC_ANSI 138 | # define mlist_rm(list, index) managed_list_remove(list, index) 139 | #else 140 | # define mlist_rm(list, index) managed_list_remove(MC_ASSERT_IS_MLIST(list), index) 141 | #endif 142 | static int managed_list_remove(const void *ptr, size_t index) 143 | { 144 | mlist_t(void) *list = (void *)ptr; 145 | struct managed_PointerInfo *datainfo = _mcinternal_ptrinfo(*list), *listinfo = _mcinternal_ptrinfo(list); 146 | if (datainfo == NULL) return 1; 147 | 148 | MC_MUTEX_LOCK(&listinfo->lock); 149 | MC_MUTEX_LOCK(&datainfo->lock); 150 | if (index >= datainfo->count) return 2; 151 | 152 | MC_MEMMOVE(((unsigned char *)*list) + index * datainfo->typesize, ((unsigned char *)*list) + (index + 1) * datainfo->typesize, (datainfo->count - index - 1) * datainfo->typesize); 153 | listinfo->count = --datainfo->count; 154 | 155 | MC_MUTEX_UNLOCK(&datainfo->lock); 156 | MC_MUTEX_UNLOCK(&listinfo->lock); 157 | return 0; 158 | } 159 | 160 | #if MC_ANSI 161 | # define mlist_get(list, index) managed_list_get(list, index) 162 | #else 163 | # define mlist_get(list, index) (mc_typeof(*list))managed_list_get(MC_ASSERT_IS_MLIST(list), index) 164 | #endif 165 | static void *managed_list_get(const void *ptr, size_t index) 166 | { 167 | mlist_t(void) *list = (void *)ptr; 168 | struct managed_PointerInfo *listinfo = _mcinternal_ptrinfo(*list); 169 | void *res = NULL; 170 | if (listinfo == NULL) return NULL; 171 | MC_MUTEX_LOCK(&listinfo->lock); 172 | if (index >= listinfo->count) return NULL; 173 | res = ((unsigned char *)*list) + index * listinfo->typesize; 174 | MC_MUTEX_UNLOCK(&listinfo->lock); 175 | return res; 176 | } 177 | 178 | #if MC_ANSI 179 | # define mlist_set(list, index, data) managed_list_set(list, index, data) 180 | #else 181 | # define mlist_set(list, index, data) managed_list_set(MC_ASSERT_IS_MLIST(list), index, MC_ASSERT_DATA_TYPE(list, data)) 182 | #endif 183 | static int managed_list_set(const void *ptr, size_t index, const void *data) 184 | { 185 | mlist_t(void) *list = (void *)ptr; 186 | struct managed_PointerInfo *listinfo = _mcinternal_ptrinfo(*list); 187 | if (listinfo == NULL) return 1; 188 | MC_MUTEX_LOCK(&listinfo->lock); 189 | if (index >= listinfo->count) return 2; 190 | MC_MEMCPY(((unsigned char *)*list) + index * listinfo->typesize, data, listinfo->typesize); 191 | MC_MUTEX_UNLOCK(&listinfo->lock); 192 | return 0; 193 | } 194 | 195 | #if MC_ANSI 196 | # define mlist_copy(list) managed_list_copy(list) 197 | #else 198 | # define mlist_copy(list) (mlist_t(mc_typeof(**list)) *)managed_list_copy(MC_ASSERT_IS_MLIST(list)) 199 | #endif 200 | static mlist_t(void) *managed_list_copy(const void *ptr) 201 | { 202 | mlist_t(void) *list = (void *)ptr, *const *ret; 203 | struct managed_PointerInfo *listinfo = _mcinternal_ptrinfo(*list); 204 | if (listinfo == NULL) return NULL; 205 | MC_MUTEX_LOCK(&listinfo->lock); 206 | ret = managed_list(listinfo->typesize, listinfo->count, listinfo->free, *list); 207 | MC_MUTEX_UNLOCK(&listinfo->lock); 208 | return ret; 209 | } 210 | 211 | #undef _mcinternal_ptrinfo 212 | 213 | #endif 214 | -------------------------------------------------------------------------------- /src/managed/mstring.h: -------------------------------------------------------------------------------- 1 | /** 2 | * Copyright (C) 2023 Amrit Bhogal 3 | * 4 | * This file is part of ManagedC. 5 | * 6 | * ManagedC is free software: you can redistribute it and/or modify 7 | * it under the terms of the GNU General Public License as published by 8 | * the Free Software Foundation, either version 3 of the License, or 9 | * (at your option) any later version. 10 | * 11 | * ManagedC is distributed in the hope that it will be useful, 12 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 13 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 14 | * GNU General Public License for more details. 15 | * 16 | * You should have received a copy of the GNU General Public License 17 | * along with ManagedC. If not, see . 18 | */ 19 | 20 | #if !defined(MANAGEDC_MSTRING) 21 | #define MANAGEDC_MSTRING 22 | 23 | #include 24 | 25 | #include "managed.h" 26 | 27 | #if !defined(MSTRING_CHAR_T) && !defined(mc_char_t) 28 | #define MSTRING_CHAR_T char 29 | typedef MSTRING_CHAR_T mc_char_t; 30 | #endif 31 | 32 | typedef mc_char_t mstring_t; 33 | 34 | #define _mcinternal_ptrinfo(ptr) ((struct managed_PointerInfo *)managed_info_of(ptr)) 35 | #if defined(MC_MUTEX) 36 | # define _mcinternal_lock(ptr) (MC_MUTEX_LOCK(&_mcinternal_ptrinfo(ptr)->lock)) 37 | # define _mcinternal_unlock(ptr) (MC_MUTEX_UNLOCK(&_mcinternal_ptrinfo(ptr)->lock)) 38 | #else 39 | # define _mcinternal_lock(ptr) 40 | # define _mcinternal_unlock(ptr) 41 | #endif 42 | 43 | 44 | #define mstrlen(str) mc_countof(str) 45 | 46 | /** 47 | * Creates metadata for the str pointer, does not copy any bytes 48 | */ 49 | static mstring_t *mc_nullable managed_string(const mc_char_t *mc_nonnull str, size_t len) 50 | { 51 | mstring_t *s = managed_allocate(len + 1, sizeof(mc_char_t), NULL, NULL); 52 | if (s == NULL) return NULL; 53 | _mcinternal_lock(s); 54 | MC_MEMCPY(s, str, len); 55 | s[len] = '\0'; 56 | 57 | _mcinternal_ptrinfo(s)->count--; /* Don't count the null terminator */ 58 | _mcinternal_unlock(s); 59 | return s; 60 | } 61 | static mstring_t *mc_nullable mstr(const mc_char_t *mc_nonnull str) 62 | { return managed_string(str, strlen(str)); } 63 | 64 | 65 | /** 66 | * Duplicates the managed string str 67 | */ 68 | static mstring_t *mc_nullable managed_string_duplicate(const mstring_t *mc_nonnull str) 69 | { 70 | long int length = mstrlen(str); 71 | if (length == -1) return NULL; 72 | return managed_string(str, (size_t)length); 73 | } 74 | static mstring_t *mc_nullable mstrdup(const mstring_t *mc_nonnull str) 75 | { return managed_string_duplicate(str); } 76 | 77 | #define mstrcat_unsafe(str1, str2) managed_string_concatenate(str1, str2, mstrlen(str2) < 0 ? strlen(str2) : mstrlen(str2)) 78 | #define mstrcat(str1, str2) managed_string_concatenate(str1, str2, strlen(str2)) 79 | #define mstrncat(str1, str2, len) managed_string_concatenate(str1, str2, len) 80 | #define mstrmcat(str1, str2) managed_string_concatenate(str1, mstrlen(s2)) 81 | /** 82 | * Allocates a new string of needed size, and concatenates the 2 strings into it 83 | */ 84 | static mstring_t *mc_nullable managed_string_concatenate(mstring_t *mc_nonnull s1, const mc_char_t *mc_nonnull s2, size_t s2len) 85 | { 86 | mstring_t *s = NULL; 87 | long int s1len = mstrlen(s1), total; 88 | if (s1len < 1) return NULL; 89 | total = (long int)s1len + (long int)s2len; 90 | 91 | s = managed_allocate((size_t)total + 1, sizeof(mc_char_t), NULL, NULL); 92 | if (s == NULL) return NULL; 93 | _mcinternal_lock(s); 94 | _mcinternal_lock(s1); 95 | 96 | MC_MEMCPY(s, s1, (size_t)s1len); /* It cannot be a param for data because that assumes that sizeof(data) == count * typesize */ 97 | MC_MEMCPY((mstring_t *)(s + s1len), s2, s2len); 98 | ((mstring_t *)s)[total] = '\0'; 99 | 100 | _mcinternal_ptrinfo(s)->count--; /* Don't count the null terminator */ 101 | 102 | 103 | _mcinternal_unlock(s1); 104 | _mcinternal_unlock(s); 105 | return s; 106 | } 107 | 108 | 109 | /** 110 | * Compares each character of both strings, returning 0 if not equal, and 1 if equal 111 | */ 112 | static int managed_string_equals(mstring_t *mc_nonnull s1, const mc_char_t *mc_nonnull s2, size_t s2len) 113 | { 114 | long int s1len = mstrlen(s1), i = 0, managed = 1; 115 | if (s1len == -1) { 116 | s1len = (long int)strlen(s1); 117 | managed = 0; 118 | } else _mcinternal_lock(s1); 119 | 120 | if (s1len != (long int)s2len) return 0; 121 | 122 | for (i = 0; i < s1len; i++) 123 | if (((mc_char_t *)s1)[i] != ((const mc_char_t *)s2)[i]) return 0; 124 | 125 | if (managed) 126 | _mcinternal_unlock(s1); 127 | return 1; 128 | } 129 | 130 | static int mstreq_unsafe(mstring_t *mc_nonnull s1, const mc_char_t *mc_nonnull s2) 131 | { 132 | long int s1len = mstrlen(s1), s2len = mstrlen(s2), i = 0, s1_managed = 1, s2_managed = 1; 133 | if (s1len == -1) { 134 | s1len = (long int)strlen(s1); 135 | s1_managed = 0; 136 | } else _mcinternal_lock(s1); 137 | 138 | if (s2len == -1) { 139 | s2len = (long int)strlen(s2); 140 | s2_managed = 0; 141 | } else _mcinternal_lock(s2); 142 | 143 | if (s1len != s2len) return 0; 144 | 145 | for (i = 0; i < s1len; i++) 146 | if (((mc_char_t *)s1)[i] != ((const mc_char_t *)s2)[i]) return 0; 147 | 148 | /*INTENTIONAL INDENTATION*/ 149 | if (s1_managed) _mcinternal_unlock(s1); 150 | if (s2_managed) _mcinternal_unlock(s2); 151 | 152 | return 1; 153 | } 154 | 155 | static int mstreq(mstring_t *mc_nonnull s1, const mc_char_t *mc_nonnull s2) 156 | { return managed_string_equals(s1, s2, strlen(s2)); } 157 | 158 | static int mstrneq(mstring_t *mc_nonnull s1, const mc_char_t *mc_nonnull s2, size_t len) 159 | { return managed_string_equals(s1, s2, len); } 160 | 161 | #endif 162 | -------------------------------------------------------------------------------- /tests/.DS_Store: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Frityet/ManagedC/d37d8cbf0900e4cb00204755585885d22fa12754/tests/.DS_Store -------------------------------------------------------------------------------- /tests/src/alloc.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | 4 | #include "managed.h" 5 | #include "test.h" 6 | 7 | declaretest(alloc) 8 | { 9 | int *val = NULL; 10 | 11 | { 12 | int *i = mc_alloc(int, NULL); 13 | ASSERT(i != NULL, "Could not allocate 4 bytes!"); 14 | 15 | *i = 512; 16 | val = mc_ref(i); 17 | mc_free(i); 18 | } 19 | 20 | ASSERT(*val == 512, "Value did not set!"); 21 | 22 | return success; 23 | } 24 | -------------------------------------------------------------------------------- /tests/src/auto.c: -------------------------------------------------------------------------------- 1 | #include "managed.h" 2 | 3 | #include "test.h" 4 | 5 | declaretest(mcauto) 6 | { 7 | # if MC_ANSI 8 | return success; 9 | #else 10 | # define auto mc_auto 11 | 12 | auto int *i = mc_array(int, 10, NULL); 13 | ASSERT(i != NULL, "Allocation failed!"); 14 | 15 | { 16 | mc_auto int *i2 = mc_ref(i); 17 | *i2 = 4; 18 | } 19 | 20 | ASSERT(*i == 4, "Invaid value!"); 21 | 22 | return success; 23 | #endif 24 | } 25 | 26 | 27 | -------------------------------------------------------------------------------- /tests/src/defer.c: -------------------------------------------------------------------------------- 1 | #include "managed.h" 2 | #include "test.h" 3 | 4 | #include 5 | #include 6 | 7 | declaretest(mcdefer) 8 | { 9 | #if !MC_LLVM 10 | return success; 11 | #else 12 | 13 | # define auto mc_auto 14 | # define defer mc_defer 15 | # define nocapture mc_nocapture 16 | 17 | nocapture bool fail = false; 18 | auto int *buf = mc_array(int, _JBLEN, NULL); 19 | nocapture int jmp = setjmp(buf); 20 | if (fail) ASSERT(false, "Defer did not complete properly!"); 21 | 22 | int i = 5; 23 | defer { 24 | if (i != 5) { 25 | fail = true; 26 | longjmp(buf, true); 27 | } 28 | }; 29 | i = 2; 30 | 31 | nocapture int i_nc = 5; 32 | defer { 33 | if (i == 5) { 34 | fail = true; 35 | longjmp(buf, true); 36 | } 37 | }; 38 | 39 | return success; 40 | #endif 41 | } 42 | -------------------------------------------------------------------------------- /tests/src/destructor.c: -------------------------------------------------------------------------------- 1 | #include "test.h" 2 | 3 | struct MyStruct { 4 | int *array; 5 | mstring_t *str; 6 | mlist_t(int) *list; 7 | }; 8 | 9 | static void MyStruct_destroy(struct MyStruct *obj) 10 | { 11 | mc_free(obj->array); 12 | mc_free(obj->str); 13 | mc_free(obj->list); 14 | } 15 | 16 | static struct MyStruct *MyStruct_create(const char *str, size_t arrsiz, int data[]) 17 | { 18 | struct MyStruct *self = mc_alloc(struct MyStruct, &MyStruct_destroy); 19 | ASSERT(self != NULL, "Could not allocate self!"); 20 | 21 | self->str = mstr(str); 22 | ASSERT(self->str != NULL, "Could not allocate str!"); 23 | 24 | self->array = mc_array(int, arrsiz, NULL); 25 | ASSERT(self->array != NULL, "Could not allocate array!"); 26 | memcpy(self->array, data, arrsiz); 27 | 28 | self->list = mlist_new(int, NULL); 29 | 30 | return self; 31 | } 32 | 33 | declaretest(destructor) 34 | { 35 | int arr[] = { 1, 2, 3, 4, 5, 6, 7, 8, 9, 10 }; 36 | struct MyStruct *obj = MyStruct_create("test", 10, arr); 37 | ASSERT(obj != NULL, "Could not create obj"); 38 | 39 | 40 | mc_free(obj); 41 | return success; 42 | } 43 | -------------------------------------------------------------------------------- /tests/src/hashmap.c: -------------------------------------------------------------------------------- 1 | #include "managed/mhashmap.h" 2 | #include "test.h" 3 | 4 | declaretest(hashmap) 5 | { 6 | mhashmap_t(mstring_t *, int) *map; 7 | 8 | return success; 9 | } 10 | -------------------------------------------------------------------------------- /tests/src/list.c: -------------------------------------------------------------------------------- 1 | #include "test.h" 2 | 3 | #include 4 | #include 5 | 6 | #define LIST_SIZE (1 << 16) 7 | 8 | declaretest(list) 9 | { 10 | mlist_t(size_t) *list = mlist_new(size_t, NULL); 11 | size_t i = 0, listlen = 0; 12 | 13 | ASSERT(list != NULL, "Could not allocate list!"); 14 | for (i = 0; i < LIST_SIZE; i++) 15 | ASSERT(mlist_add(list, &i) == 0, "Could not add item!"); 16 | 17 | listlen = (size_t)mc_countof(list); 18 | ASSERT(listlen == LIST_SIZE, "Count did not match!"); 19 | 20 | for (i = 0; i < listlen; i++) { 21 | size_t *val = mlist_get(list, i); 22 | ASSERT(val != NULL, "Could not get value!"); 23 | 24 | ASSERT(*val == i, "Value did not match!"); 25 | } 26 | 27 | mlist_set(list, 0, &i); 28 | ASSERT(*(size_t *)mlist_get(list, 0) == LIST_SIZE, "Value did not set!"); 29 | 30 | mlist_rm(list, 0); 31 | ASSERT(*(size_t *)mlist_get(list, 0) != LIST_SIZE, "Value did not remove!"); 32 | ASSERT((size_t)mc_countof(list) == LIST_SIZE - 1, "List length did not lower!"); 33 | 34 | mc_free(list); 35 | 36 | return success; 37 | } 38 | -------------------------------------------------------------------------------- /tests/src/main.c: -------------------------------------------------------------------------------- 1 | #include "test.h" 2 | 3 | #include 4 | #include 5 | #include 6 | #include 7 | #include 8 | 9 | 10 | 11 | int main(void) 12 | { 13 | size_t i = 0, failc = 0; 14 | struct Test tests[16]; 15 | 16 | extern struct Test TESTNAME(alloc), TESTNAME(realloc), TESTNAME(list), TESTNAME(string), TESTNAME(linkedlist), TESTNAME(mcauto), TESTNAME(mcdefer), TESTNAME(destructor), TESTNAME(threads); 17 | tests[__COUNTER__] = TESTNAME(alloc); 18 | tests[__COUNTER__] = TESTNAME(realloc); 19 | tests[__COUNTER__] = TESTNAME(string); 20 | tests[__COUNTER__] = TESTNAME(mcauto); 21 | tests[__COUNTER__] = TESTNAME(mcdefer); 22 | tests[__COUNTER__] = TESTNAME(list); 23 | tests[__COUNTER__] = TESTNAME(destructor); 24 | tests[__COUNTER__] = TESTNAME(threads); 25 | 26 | printf("Running %d tests\n", __COUNTER__); 27 | 28 | for (i = 0; i < __COUNTER__ - 1; i++) { 29 | size_t charc = (size_t)printf("Running test %s...", tests[i].name); 30 | putchar('\t'); 31 | if (charc <= sizeof("Running test destructor...") - 2) putchar('\t'); 32 | bool res = tests[i].test(); 33 | if (res == failure) { 34 | failc++; 35 | fprintf(stderr, "\x1b[31m[Fail]\x1b[0m\n"); 36 | } else printf("\x1b[32m[Success]\x1b[0m\n"); 37 | } 38 | 39 | if (failc > 0) { 40 | fprintf(stderr, "\x1b[31mFailed %ld tests!\x1b[0m\n", failc); 41 | return EXIT_FAILURE; 42 | } 43 | printf("\x1b[32mSuccessfully ran all tests!\x1b[0m\n"); 44 | } 45 | -------------------------------------------------------------------------------- /tests/src/realloc.c: -------------------------------------------------------------------------------- 1 | #include "test.h" 2 | 3 | declaretest(realloc) 4 | { 5 | int arr[] = { 10, 20, 30, 40, 50, 60, 70, 80, 90, 100 }; 6 | int *alloc = managed_allocate(10, sizeof(int), NULL, arr), *new = NULL, i = 0; 7 | ASSERT(alloc != NULL, "Could not allocate 10 ints!"); 8 | for (i = 0; i < 10; i++) 9 | ASSERT(alloc[i] == (i + 1) * 10, "Value did not set!"); 10 | 11 | new = managed_copy(alloc, 20); 12 | ASSERT(new != NULL, "Could not allocate 20 ints!"); 13 | for (i = 0; i < 10; i++) 14 | ASSERT(alloc[i] == (i + 1) * 10, "Value did not set!"); 15 | 16 | memcpy(new + 10, arr, sizeof(arr)); 17 | ASSERT(new[10] == 10, "Value did not set!"); 18 | 19 | mc_free(alloc); 20 | mc_free(new); 21 | return success; 22 | } 23 | -------------------------------------------------------------------------------- /tests/src/string.c: -------------------------------------------------------------------------------- 1 | #include "managed.h" 2 | #include "test.h" 3 | 4 | declaretest(string) 5 | { 6 | mstring_t *str = mstr("Hello, "), *full = NULL; 7 | ASSERT(str != NULL, "Could not allocate string!"); 8 | 9 | { 10 | mstring_t *str2 = mstrcat(str, "World!"); 11 | ASSERT(str2 != NULL, "Could not allocate string!"); 12 | full = mc_ref(str2); 13 | mc_free(str); 14 | mc_free(str2); 15 | } 16 | 17 | ASSERT(full != NULL, "Could not allocate string!"); 18 | ASSERT(mstrlen(full) == strlen("Hello, World!"), "Length did not match!"); 19 | ASSERT((bool)mstreq(full, "Hello, World!"), "String did not match!"); 20 | 21 | mc_free(full); 22 | return success; 23 | } 24 | -------------------------------------------------------------------------------- /tests/src/test.h: -------------------------------------------------------------------------------- 1 | #if !defined(MANAGED_TEST) 2 | #define MANAGED_TEST 3 | 4 | #include 5 | #include 6 | 7 | #include "managed/mstring.h" 8 | #include "managed/mlist.h" 9 | 10 | #if __STDC_VERSION__ < 199901L 11 | typedef enum { false, true } bool; 12 | #else 13 | # include 14 | #endif 15 | 16 | enum { success, failure }; 17 | 18 | typedef bool Test_f(void); 19 | 20 | struct Test { 21 | const char *name; 22 | Test_f *test; 23 | }; 24 | 25 | #define declaretest(t) static bool _##t##_test(void); const struct Test __test_##t = { #t, _##t##_test }; static bool _##t##_test(void) 26 | #define TESTNAME(t) __test_##t 27 | 28 | static bool assert(bool expr, const char *err, const char *strexpr, const char *file, int line) 29 | { 30 | if (!expr) { 31 | fprintf(stderr, "Assertion { %s } failed located at [%s:%d]\nError: %s\n", strexpr, file, line, err); 32 | return false; 33 | } 34 | return true; 35 | } 36 | 37 | 38 | #define ASSERT(expr, msg) do { if (!assert((expr), msg, #expr, __FILE__, __LINE__)) return 0; } while (0) 39 | 40 | #endif 41 | -------------------------------------------------------------------------------- /tests/src/threads.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | 5 | #include "managed.h" 6 | #include "managed/mlist.h" 7 | #include "test.h" 8 | 9 | enum { 10 | THREAD_COUNT = 16 11 | }; 12 | 13 | static void *add_100_nums(mlist_t(size_t) *list) 14 | { 15 | size_t i = 0; 16 | for (; i < 100; i++) 17 | ASSERT(mlist_add(list, &i) == 0, "Could not add item to list!"); 18 | 19 | mc_free(list); 20 | return NULL; 21 | } 22 | 23 | static void *read_and_add(mlist_t(size_t) *list) 24 | { 25 | size_t i = 0; 26 | long int c = mc_countof(list); 27 | ASSERT(c > 0, "Could not get count!"); 28 | for (i = 0; i < (size_t)c; i++) { 29 | size_t *numptr = mlist_get(list, i); 30 | ASSERT(numptr != NULL, "Could not get element of list!"); 31 | i += *numptr; 32 | } 33 | 34 | mc_free(list); 35 | return NULL; 36 | } 37 | 38 | declaretest(threads) 39 | { 40 | pthread_t threads[THREAD_COUNT]; 41 | mlist_t(size_t) *numlist = mlist_new(size_t, NULL); 42 | size_t i = 0; 43 | 44 | for (i = 0; i < THREAD_COUNT / 2; i++) 45 | ASSERT(pthread_create(&threads[i], NULL, (void *(*)(void *))&add_100_nums, mc_ref((void *)numlist)) == 0, 46 | "Could not create thread!"); 47 | 48 | for (; i < THREAD_COUNT; i++) 49 | ASSERT(pthread_create(&threads[i], NULL, (void *(*)(void *))&read_and_add, mc_ref((void *)numlist)) == 0, 50 | "Could not create thread!"); 51 | 52 | for (i = 0; i < THREAD_COUNT; i++) 53 | ASSERT(pthread_join(threads[i], NULL) == 0, "Could not join thread!"); 54 | 55 | ASSERT(mc_countof(numlist) == (THREAD_COUNT / 2) * 100, "Numbers were not added!"); 56 | 57 | mc_free(numlist); 58 | return success; 59 | } 60 | -------------------------------------------------------------------------------- /tests/xmake.lua: -------------------------------------------------------------------------------- 1 | option("ansi") 2 | do 3 | set_default(false) 4 | set_showmenu(true) 5 | 6 | set_description("Compiles tests with ANSI-C89 standards, which disables the mc_auto and mc_defer tests") 7 | end 8 | option_end() 9 | 10 | option("thread-sanitizer") 11 | do 12 | set_default(false) 13 | set_showmenu(true) 14 | 15 | set_description("Compiles tests with the thread sanitizer, instead of the regular address,leak,undefined") 16 | end 17 | option_end() 18 | 19 | local CFLAGS = { 20 | "-Wall", "-Wextra", "-Werror", 21 | "-Weverything", 22 | "-Wno-unused-parameter", "-Wno-unused-variable", "-Wno-unused-function", "-Wno-unused-macros", 23 | "-Wno-missing-variable-declarations", 24 | "-Wno-keyword-macro", 25 | "-Wno-reserved-identifier", 26 | "-Wno-comma", 27 | "-Wno-cast-qual", 28 | "-Wno-bad-function-cast", 29 | "-Wno-atomic-implicit-seq-cst", 30 | "-Wno-declaration-after-statement", 31 | } 32 | 33 | local SANITIZERS = {} 34 | 35 | if has_config("ansi") then 36 | add_cflags { 37 | "-ansi", 38 | "-Wpedantic" 39 | } 40 | 41 | set_languages("c89") 42 | add_defines("__STRICT_ANSI__") 43 | else 44 | CFLAGS[#CFLAGS + 1] = "-Wno-gnu-statement-expression" 45 | CFLAGS[#CFLAGS + 1] = "-Wno-nullability-extension" 46 | CFLAGS[#CFLAGS + 1] = "-Wno-vla" 47 | 48 | add_undefines("__STRICT_ANSI__") 49 | set_languages("gnulatest") 50 | end 51 | 52 | if has_config("thread-sanitizer") then 53 | SANITIZERS[#SANITIZERS + 1] = "thread" 54 | else 55 | SANITIZERS[#SANITIZERS + 1] = "address" 56 | -- SANITIZERS[#SANITIZERS + 1] = "leak" 57 | SANITIZERS[#SANITIZERS + 1] = "undefined" 58 | end 59 | 60 | add_rules("mode.debug", "mode.release") 61 | 62 | 63 | target("tests") 64 | do 65 | set_kind("binary") 66 | add_files("src/**.c") 67 | 68 | add_cflags(CFLAGS) 69 | 70 | add_options("ansi") 71 | if is_mode("debug") then 72 | add_cflags("-fno-omit-frame-pointer") 73 | for i, v in ipairs(SANITIZERS) do 74 | add_cflags("-fsanitize=" .. v) 75 | add_ldflags("-fsanitize=" .. v) 76 | end 77 | end 78 | 79 | add_deps("ManagedC") 80 | end 81 | target_end() 82 | -------------------------------------------------------------------------------- /xmake.lua: -------------------------------------------------------------------------------- 1 | add_rules("mode.debug", "mode.release") 2 | 3 | target("ManagedC") 4 | do 5 | set_kind("headeronly") 6 | add_headerfiles("src/**.h") 7 | add_includedirs("src/", { public = true }) 8 | end 9 | target_end() 10 | 11 | includes("tests") 12 | --------------------------------------------------------------------------------