├── .github ├── ISSUE_TEMPLATE │ ├── bug-report.md │ ├── config.yml │ └── feature-request.md ├── pull_request_template.md └── workflows │ ├── codeql.yml │ └── main.yml ├── .gitignore ├── CMakeLists.txt ├── CODE_OF_CONDUCT.md ├── CONTRIBUTING.md ├── LICENSE.md ├── Makefile ├── README.md ├── SECURITY.md ├── _config.yml ├── bench.c ├── buddy_alloc.h ├── compile_flags.txt ├── test-fuzz.c ├── testcxx.cpp └── tests.c /.github/ISSUE_TEMPLATE/bug-report.md: -------------------------------------------------------------------------------- 1 | --- 2 | name: Bug report 3 | about: Use this template to report bugs, performance and usability issues 4 | title: '' 5 | labels: '' 6 | assignees: spaskalev 7 | 8 | --- 9 | 10 | **Describe the bug** 11 | A clear and concise description of what the bug is. 12 | 13 | **Expected behavior** 14 | A clear and concise description of what you expected to happen. 15 | 16 | **To Reproduce** 17 | Provide a minimal code example that demonstrates the bug. 18 | -------------------------------------------------------------------------------- /.github/ISSUE_TEMPLATE/config.yml: -------------------------------------------------------------------------------- 1 | blank_issues_enabled: false 2 | contact_links: 3 | - name: Project discussions 4 | url: https://github.com/spaskalev/buddy_alloc/discussions 5 | about: Please ask and answer questions here. 6 | - name: Contact the author 7 | url: https://github.com/spaskalev 8 | about: Use email or find me on discord (spaskalev) 9 | -------------------------------------------------------------------------------- /.github/ISSUE_TEMPLATE/feature-request.md: -------------------------------------------------------------------------------- 1 | --- 2 | name: Feature request 3 | about: Use this template to report feature requests. 4 | title: '' 5 | labels: '' 6 | assignees: spaskalev 7 | 8 | --- 9 | 10 | **Describe the request** 11 | A clear and concise description of the required functionality. 12 | 13 | **Justification** 14 | Why is it important for the project and its users. 15 | 16 | **API Mock-up** 17 | If you envision a specific API please indicate it here. 18 | -------------------------------------------------------------------------------- /.github/pull_request_template.md: -------------------------------------------------------------------------------- 1 | 2 | -------------------------------------------------------------------------------- /.github/workflows/codeql.yml: -------------------------------------------------------------------------------- 1 | # For most projects, this workflow file will not need changing; you simply need 2 | # to commit it to your repository. 3 | # 4 | # You may wish to alter this file to override the set of languages analyzed, 5 | # or to provide custom queries or build logic. 6 | # 7 | # ******** NOTE ******** 8 | # We have attempted to detect the languages in your repository. Please check 9 | # the `language` matrix defined below to confirm you have the correct set of 10 | # supported CodeQL languages. 11 | # 12 | name: "CodeQL" 13 | 14 | on: 15 | push: 16 | branches: [ "main" ] 17 | pull_request: 18 | branches: [ "main" ] 19 | schedule: 20 | - cron: '27 4 * * 5' 21 | 22 | jobs: 23 | analyze: 24 | name: Analyze 25 | # Runner size impacts CodeQL analysis time. To learn more, please see: 26 | # - https://gh.io/recommended-hardware-resources-for-running-codeql 27 | # - https://gh.io/supported-runners-and-hardware-resources 28 | # - https://gh.io/using-larger-runners 29 | # Consider using larger runners for possible analysis time improvements. 30 | runs-on: ${{ (matrix.language == 'swift' && 'macos-latest') || 'ubuntu-latest' }} 31 | timeout-minutes: ${{ (matrix.language == 'swift' && 120) || 360 }} 32 | permissions: 33 | actions: read 34 | contents: read 35 | security-events: write 36 | 37 | strategy: 38 | fail-fast: false 39 | matrix: 40 | language: [ 'c-cpp' ] 41 | # CodeQL supports [ 'c-cpp', 'csharp', 'go', 'java-kotlin', 'javascript-typescript', 'python', 'ruby', 'swift' ] 42 | # Use only 'java-kotlin' to analyze code written in Java, Kotlin or both 43 | # Use only 'javascript-typescript' to analyze code written in JavaScript, TypeScript or both 44 | # Learn more about CodeQL language support at https://aka.ms/codeql-docs/language-support 45 | 46 | steps: 47 | - name: Checkout repository 48 | uses: actions/checkout@v4 49 | 50 | # Initializes the CodeQL tools for scanning. 51 | - name: Initialize CodeQL 52 | uses: github/codeql-action/init@v3 53 | with: 54 | languages: ${{ matrix.language }} 55 | # If you wish to specify custom queries, you can do so here or in a config file. 56 | # By default, queries listed here will override any specified in a config file. 57 | # Prefix the list here with "+" to use these queries and those in the config file. 58 | 59 | # For more details on CodeQL's query packs, refer to: https://docs.github.com/en/code-security/code-scanning/automatically-scanning-your-code-for-vulnerabilities-and-errors/configuring-code-scanning#using-queries-in-ql-packs 60 | # queries: security-extended,security-and-quality 61 | 62 | 63 | # Autobuild attempts to build any compiled languages (C/C++, C#, Go, Java, or Swift). 64 | # If this step fails, then you should remove it and run the build manually (see below) 65 | - name: Autobuild 66 | uses: github/codeql-action/autobuild@v3 67 | 68 | # ℹ️ Command-line programs to run using the OS shell. 69 | # 📚 See https://docs.github.com/en/actions/using-workflows/workflow-syntax-for-github-actions#jobsjob_idstepsrun 70 | 71 | # If the Autobuild fails above, remove it and uncomment the following three lines. 72 | # modify them (or add more) to build your code if your project, please refer to the EXAMPLE below for guidance. 73 | 74 | # - run: | 75 | # echo "Run, Build Application using script" 76 | # ./location_of_script_within_repo/buildscript.sh 77 | 78 | - name: Perform CodeQL Analysis 79 | uses: github/codeql-action/analyze@v3 80 | with: 81 | category: "/language:${{matrix.language}}" 82 | -------------------------------------------------------------------------------- /.github/workflows/main.yml: -------------------------------------------------------------------------------- 1 | name: cicd 2 | on: 3 | push: 4 | branches: [ main ] 5 | pull_request: 6 | branches: [ main ] 7 | jobs: 8 | build-ubuntu: 9 | runs-on: ubuntu-22.04 10 | steps: 11 | - uses: actions/checkout@v4 12 | - name: setup 13 | run: sudo apt-get update && sudo apt-get -y -qq install clang-tidy cppcheck cflow spell gcc-powerpc64-linux-gnu gcc-powerpc-linux-gnu gcc-aarch64-linux-gnu gcc-i686-linux-gnu qemu-user-binfmt 14 | - name: test-main 15 | run: make LLVM_VERSION=14 CC=clang 16 | working-directory: . 17 | - name: bench-main 18 | run: make LLVM_VERSION=14 CC=clang bench 19 | working-directory: . 20 | - name: test-multiplatform 21 | run: make test-multiplatform CC=clang 22 | working-directory: . 23 | - name: test-cpp-translation-unit 24 | run: make LLVM_VERSION=14 CC=clang CXX=clang++ test-cpp-translation-unit 25 | working-directory: . 26 | - uses: actions/upload-artifact@v4 27 | if: failure() 28 | with: 29 | name: buddy_alloc.h.gcov 30 | path: ./buddy_alloc.h.gcov 31 | if-no-files-found: warn 32 | build-windows: 33 | runs-on: windows-latest 34 | strategy: 35 | matrix: 36 | arch: [amd64, amd64_x86, amd64_arm64] 37 | steps: 38 | - uses: actions/checkout@v4 39 | - uses: ilammy/msvc-dev-cmd@v1 40 | with: 41 | arch: ${{ matrix.arch }} 42 | - name: Build Windows target 43 | run: | 44 | cmake -G "NMake Makefiles" . 45 | nmake 46 | - name: Run Tests 47 | # Skip tests on ARM (cross-compilation) 48 | if: ${{ !contains( matrix.arch, 'amd64_arm' ) }} 49 | run: | 50 | ./buddy_tests 51 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | # Prerequisites 2 | *.d 3 | 4 | # Object files 5 | *.o 6 | *.ko 7 | *.obj 8 | *.elf 9 | 10 | # Linker output 11 | *.ilk 12 | *.map 13 | *.exp 14 | 15 | # Precompiled Headers 16 | *.gch 17 | *.pch 18 | 19 | # Libraries 20 | *.lib 21 | *.a 22 | *.la 23 | *.lo 24 | 25 | # Shared objects (inc. Windows DLLs) 26 | *.dll 27 | *.so 28 | *.so.* 29 | *.dylib 30 | 31 | # Executables 32 | *.exe 33 | *.out 34 | *.app 35 | *.i*86 36 | *.x86_64 37 | *.hex 38 | 39 | # Debug files 40 | *.dSYM/ 41 | *.su 42 | *.idb 43 | *.pdb 44 | 45 | # Kernel Module Compile Results 46 | *.mod* 47 | *.cmd 48 | .tmp_versions/ 49 | modules.order 50 | Module.symvers 51 | Mkfile.old 52 | dkms.conf 53 | 54 | # gcov data 55 | *.gcov 56 | *.gcda 57 | *.gcno 58 | *.static 59 | 60 | # Perf 61 | bench 62 | 63 | # Test 64 | tests 65 | 66 | # cmake output dir 67 | out/* 68 | 69 | # visual studio metadata 70 | .vs/* 71 | 72 | # visual studio cmake settings 73 | CMakeSettings.json 74 | -------------------------------------------------------------------------------- /CMakeLists.txt: -------------------------------------------------------------------------------- 1 | cmake_minimum_required(VERSION 3.10) 2 | project(buddy_alloc) 3 | set(C_STANDARD C99) 4 | 5 | # Compile C tests 6 | project(buddy_tests) 7 | set(C_STANDARD C99) 8 | set(SOURCE_FILES tests.c) 9 | add_executable(buddy_tests ${SOURCE_FILES}) 10 | 11 | # Compile CPP translation unit 12 | project(buddy_cpp_translation_unit) 13 | set(CXX_STANDARD C++11) 14 | set(SOURCE_FILES testcxx.cpp) 15 | add_executable(buddy_cpp_translation_unit ${SOURCE_FILES}) 16 | 17 | # Compile benchmark 18 | project(buddy_bench) 19 | set(C_STANDARD C99) 20 | set(SOURCE_FILES bench.c) 21 | add_executable(buddy_bench ${SOURCE_FILES}) -------------------------------------------------------------------------------- /CODE_OF_CONDUCT.md: -------------------------------------------------------------------------------- 1 | # Contributor Covenant Code of Conduct 2 | 3 | ## Our Pledge 4 | 5 | We as members, contributors, and leaders pledge to make participation in our 6 | community a harassment-free experience for everyone, regardless of age, body 7 | size, visible or invisible disability, ethnicity, sex characteristics, gender 8 | identity and expression, level of experience, education, socio-economic status, 9 | nationality, personal appearance, race, religion, or sexual identity 10 | and orientation. 11 | 12 | We pledge to act and interact in ways that contribute to an open, welcoming, 13 | diverse, inclusive, and healthy community. 14 | 15 | ## Our Standards 16 | 17 | Examples of behavior that contributes to a positive environment for our 18 | community include: 19 | 20 | * Demonstrating empathy and kindness toward other people 21 | * Being respectful of differing opinions, viewpoints, and experiences 22 | * Giving and gracefully accepting constructive feedback 23 | * Accepting responsibility and apologizing to those affected by our mistakes, 24 | and learning from the experience 25 | * Focusing on what is best not just for us as individuals, but for the 26 | overall community 27 | 28 | Examples of unacceptable behavior include: 29 | 30 | * The use of sexualized language or imagery, and sexual attention or 31 | advances of any kind 32 | * Trolling, insulting or derogatory comments, and personal or political attacks 33 | * Public or private harassment 34 | * Publishing others' private information, such as a physical or email 35 | address, without their explicit permission 36 | * Other conduct which could reasonably be considered inappropriate in a 37 | professional setting 38 | 39 | ## Enforcement Responsibilities 40 | 41 | Community leaders are responsible for clarifying and enforcing our standards of 42 | acceptable behavior and will take appropriate and fair corrective action in 43 | response to any behavior that they deem inappropriate, threatening, offensive, 44 | or harmful. 45 | 46 | Community leaders have the right and responsibility to remove, edit, or reject 47 | comments, commits, code, wiki edits, issues, and other contributions that are 48 | not aligned to this Code of Conduct, and will communicate reasons for moderation 49 | decisions when appropriate. 50 | 51 | ## Scope 52 | 53 | This Code of Conduct applies within all community spaces, and also applies when 54 | an individual is officially representing the community in public spaces. 55 | Examples of representing our community include using an official e-mail address, 56 | posting via an official social media account, or acting as an appointed 57 | representative at an online or offline event. 58 | 59 | ## Enforcement 60 | 61 | Instances of abusive, harassing, or otherwise unacceptable behavior may be 62 | reported to the community leaders responsible for enforcement at 63 | https://github.com/spaskalev/buddy_alloc/discussions. 64 | All complaints will be reviewed and investigated promptly and fairly. 65 | 66 | All community leaders are obligated to respect the privacy and security of the 67 | reporter of any incident. 68 | 69 | ## Enforcement Guidelines 70 | 71 | Community leaders will follow these Community Impact Guidelines in determining 72 | the consequences for any action they deem in violation of this Code of Conduct: 73 | 74 | ### 1. Correction 75 | 76 | **Community Impact**: Use of inappropriate language or other behavior deemed 77 | unprofessional or unwelcome in the community. 78 | 79 | **Consequence**: A private, written warning from community leaders, providing 80 | clarity around the nature of the violation and an explanation of why the 81 | behavior was inappropriate. A public apology may be requested. 82 | 83 | ### 2. Warning 84 | 85 | **Community Impact**: A violation through a single incident or series 86 | of actions. 87 | 88 | **Consequence**: A warning with consequences for continued behavior. No 89 | interaction with the people involved, including unsolicited interaction with 90 | those enforcing the Code of Conduct, for a specified period of time. This 91 | includes avoiding interactions in community spaces as well as external channels 92 | like social media. Violating these terms may lead to a temporary or 93 | permanent ban. 94 | 95 | ### 3. Temporary Ban 96 | 97 | **Community Impact**: A serious violation of community standards, including 98 | sustained inappropriate behavior. 99 | 100 | **Consequence**: A temporary ban from any sort of interaction or public 101 | communication with the community for a specified period of time. No public or 102 | private interaction with the people involved, including unsolicited interaction 103 | with those enforcing the Code of Conduct, is allowed during this period. 104 | Violating these terms may lead to a permanent ban. 105 | 106 | ### 4. Permanent Ban 107 | 108 | **Community Impact**: Demonstrating a pattern of violation of community 109 | standards, including sustained inappropriate behavior, harassment of an 110 | individual, or aggression toward or disparagement of classes of individuals. 111 | 112 | **Consequence**: A permanent ban from any sort of public interaction within 113 | the community. 114 | 115 | ## Attribution 116 | 117 | This Code of Conduct is adapted from the [Contributor Covenant][homepage], 118 | version 2.0, available at 119 | https://www.contributor-covenant.org/version/2/0/code_of_conduct.html. 120 | 121 | Community Impact Guidelines were inspired by [Mozilla's code of conduct 122 | enforcement ladder](https://github.com/mozilla/diversity). 123 | 124 | [homepage]: https://www.contributor-covenant.org 125 | 126 | For answers to common questions about this code of conduct, see the FAQ at 127 | https://www.contributor-covenant.org/faq. Translations are available at 128 | https://www.contributor-covenant.org/translations. 129 | -------------------------------------------------------------------------------- /CONTRIBUTING.md: -------------------------------------------------------------------------------- 1 | Thank you for your interest in this project! Feel free to file issues, send pull requests and participate in the discussions. 2 | 3 | Pull requests should match the coding style where applicable and preserve the test coverage and resiliency characteristics. 4 | -------------------------------------------------------------------------------- /LICENSE.md: -------------------------------------------------------------------------------- 1 | Copyright (C) 2021 by Stanislav Paskalev 2 | 3 | Permission to use, copy, modify, and/or distribute this software for any purpose with or without fee is hereby granted. 4 | 5 | THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. 6 | -------------------------------------------------------------------------------- /Makefile: -------------------------------------------------------------------------------- 1 | # 2 | # Copyright 2021 Stanislav Paskalev 3 | # 4 | 5 | # Disable default implicit rules 6 | MAKEFLAGS += --no-builtin-rules 7 | .SUFFIXES: 8 | 9 | LLVM_VERSION?=11 10 | CC?=clang-$(LLVM_VERSION) 11 | CXX?=clang++-$(LLVM_VERSION) 12 | CFLAGS?=-std=c99 --coverage -pg -no-pie -fno-builtin -g -O0 -Og -fstrict-aliasing -fstack-protector-all -fsanitize=undefined -fsanitize=bounds -pedantic -Wall -Wextra -Werror -Wfatal-errors -Wformat=2 -Wformat-security -Wconversion -Wcast-qual -Wnull-dereference -Wstack-protector -Warray-bounds -Warray-bounds-pointer-arithmetic -Wassign-enum -Wbad-function-cast -Wconditional-uninitialized -Wfloat-equal -Wformat-type-confusion -Widiomatic-parentheses -Wimplicit-fallthrough -Wloop-analysis -Wpointer-arith -Wshift-sign-overflow -Wshorten-64-to-32 -Wswitch-enum -Wtautological-constant-in-range-compare -Wunreachable-code-aggressive -Wthread-safety -Wthread-safety-beta -Wcomma -Wdeclaration-after-statement -D_FORTIFY_SOURCE=3 13 | CXXFLAGS?=-std=c++11 --coverage -pg -no-pie -fno-builtin -g -O0 -Og -fstrict-aliasing -fstack-protector-all -fsanitize=undefined -fsanitize=bounds -pedantic -Wall -Wextra -Wformat=2 -Wformat-security -Wconversion -Wcast-qual -Wnull-dereference -Wstack-protector -Warray-bounds -Warray-bounds-pointer-arithmetic -Wassign-enum -Wbad-function-cast -Wconditional-uninitialized -Wfloat-equal -Wformat-type-confusion -Widiomatic-parentheses -Wimplicit-fallthrough -Wloop-analysis -Wpointer-arith -Wshift-sign-overflow -Wshorten-64-to-32 -Wswitch-enum -Wtautological-constant-in-range-compare -Wunreachable-code-aggressive -Wthread-safety -Wthread-safety-beta -Wcomma -Wdeclaration-after-statement -D_FORTIFY_SOURCE=3 14 | LLVM_COV?=llvm-cov-$(LLVM_VERSION) 15 | CPPCHECK?=cppcheck 16 | 17 | TESTS_SRC=tests.c 18 | TESTCXX_SRC=testcxx.cpp 19 | LIB_SRC=buddy_alloc.h 20 | BENCH_SRC=bench.c 21 | BENCH_CFLAGS?=-O2 22 | 23 | test: tests.out 24 | rm -f *.gcda 25 | ./tests.out 26 | $(LLVM_COV) gcov -b $(TESTS_SRC) | paste -s -d ',' | sed -e 's/,,/,\n/' | cut -d ',' -f 1,2,3 27 | ! grep '#####:' $(LIB_SRC).gcov 28 | ! grep -E '^branch\s*[0-9]? never executed$$' $(LIB_SRC).gcov 29 | 30 | tests.out: $(TESTS_SRC) $(LIB_SRC) test-cppcheck check-recursion 31 | $(CC) $(CFLAGS) $(TESTS_SRC) -o $@ 32 | 33 | test-cppcheck: $(TESTS_SRC) 34 | $(CPPCHECK) --error-exitcode=1 --quiet $^ 35 | 36 | test-cpp-translation-unit: $(TESTCXX_SRC) 37 | $(CXX) $(CXXFLAGS) $(TESTCXX_SRC) -o $@ 38 | 39 | test-multiplatform: $(TESTS_SRC) 40 | # 64-bit 41 | powerpc64-linux-gnu-gcc -static $(TESTS_SRC) && ./a.out 42 | aarch64-linux-gnu-gcc -static $(TESTS_SRC) && ./a.out 43 | # 32-bit 44 | i686-linux-gnu-gcc -static -g tests.c && ./a.out 45 | powerpc-linux-gnu-gcc -static $(TESTS_SRC) && ./a.out 46 | 47 | bench: $(BENCH_SRC) $(LIB_SRC) 48 | $(CC) $(BENCH_CFLAGS) $(BENCH_SRC) -o $@ 49 | ./$@ 50 | 51 | check-recursion: $(LIB_SRC) 52 | [ $$( cflow --no-main $(LIB_SRC) | grep -c 'recursive:' ) -eq "0" ] 53 | 54 | clean: 55 | rm -f a.out *.gcda *.gcno *.gcov tests.out bench 56 | 57 | .PHONY: test clean test-cppcheck 58 | 59 | .PRECIOUS: tests.out testcxx.out 60 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # buddy_alloc 2 | A buddy memory allocator for C 3 | 4 | # Status 5 | 6 | - [![cicd](https://github.com/spaskalev/buddy_alloc/actions/workflows/main.yml/badge.svg)](https://github.com/spaskalev/buddy_alloc/actions/workflows/main.yml) [![CodeQL](https://github.com/spaskalev/buddy_alloc/actions/workflows/codeql.yml/badge.svg)](https://github.com/spaskalev/buddy_alloc/actions/workflows/codeql.yml) 7 | - [Latest release](https://github.com/spaskalev/buddy_alloc/releases/latest) 8 | 9 | ## Licensing 10 | 11 | This project is licensed under the 0BSD license. See the LICENSE.md file for details. 12 | 13 | ## Overview 14 | 15 | This is a memory allocator suitable for use in applications that require predictable allocation and deallocation behavior. The allocator's metadata is kept separate from the arena and its size is a function of the arena and minimum allocations sizes. 16 | 17 | ## Features 18 | 19 | - Bounded allocation and deallocation cost 20 | - Fixed call stack usage, no recursion, no global state 21 | - C99-compatibility for code and tests 22 | - 100% line and branch test coverage 23 | - Supports 32-bit and 64-bit platforms 24 | - Endian-agnostic, works on both LE and BE 25 | - Compiles with GCC, Clang, MSVC and Pelles C 26 | 27 | ## Usage 28 | 29 | Initializing and using the buddy allocator with metadata external to the arena is done using the `buddy_init` function. 30 | 31 | ```c 32 | size_t arena_size = 65536; 33 | /* You need space for the metadata and for the arena */ 34 | void *buddy_metadata = malloc(buddy_sizeof(arena_size)); 35 | void *buddy_arena = malloc(arena_size); 36 | struct buddy *buddy = buddy_init(buddy_metadata, buddy_arena, arena_size); 37 | 38 | /* Allocate using the buddy allocator */ 39 | void *data = buddy_malloc(buddy, 2048); 40 | /* Free using the buddy allocator */ 41 | buddy_free(buddy, data); 42 | 43 | free(buddy_metadata); 44 | free(buddy_arena); 45 | ``` 46 | 47 | Initializing and using the buddy allocator with metadata internal to the arena is done using the `buddy_embed` function. 48 | 49 | ```c 50 | size_t arena_size = 65536; 51 | /* You need space for arena and builtin metadata */ 52 | void *buddy_arena = malloc(arena_size); 53 | struct buddy *buddy = buddy_embed(buddy_arena, arena_size); 54 | 55 | /* Allocate using the buddy allocator */ 56 | void *data = buddy_malloc(buddy, 2048); 57 | /* Free using the buddy allocator */ 58 | buddy_free(buddy, data); 59 | 60 | free(buddy_arena); 61 | ``` 62 | 63 | ## Metadata sizing 64 | 65 | The following table documents the allocator metadata space requirements according to desired arena (8MB to 1024GB) and alignment/minimum allocation (64B to 8KB) sizes. The resulting values are rounded up to the nearest unit. 66 | 67 | ``` 68 | | 64B | 128B | 256B | 512B | 1KB | 2KB | 4KB | 8KB | 69 | ---------+---------+--------+--------+--------+--------+--------+--------+--------+ 70 | 8 MB | 65KB | 33KB | 17KB | 9KB | 5KB | 3KB | 2KB | 678B | 71 | 16 MB | 129KB | 65KB | 33KB | 17KB | 9KB | 5KB | 3KB | 2KB | 72 | 32 MB | 257KB | 129KB | 65KB | 33KB | 17KB | 9KB | 5KB | 3KB | 73 | 64 MB | 513KB | 257KB | 129KB | 65KB | 33KB | 17KB | 9KB | 5KB | 74 | 128 MB | 2MB | 513KB | 257KB | 129KB | 65KB | 33KB | 17KB | 9KB | 75 | 256 MB | 3MB | 2MB | 513KB | 257KB | 129KB | 65KB | 33KB | 17KB | 76 | 512 MB | 5MB | 3MB | 2MB | 513KB | 257KB | 129KB | 65KB | 33KB | 77 | 1 GB | 9MB | 5MB | 3MB | 2MB | 513KB | 257KB | 129KB | 65KB | 78 | 2 GB | 17MB | 9MB | 5MB | 3MB | 2MB | 513KB | 257KB | 129KB | 79 | 4 GB | 33MB | 17MB | 9MB | 5MB | 3MB | 2MB | 513KB | 257KB | 80 | 8 GB | 65MB | 33MB | 17MB | 9MB | 5MB | 3MB | 2MB | 513KB | 81 | 16 GB | 129MB | 65MB | 33MB | 17MB | 9MB | 5MB | 3MB | 2MB | 82 | 32 GB | 257MB | 129MB | 65MB | 33MB | 17MB | 9MB | 5MB | 3MB | 83 | 64 GB | 513MB | 257MB | 129MB | 65MB | 33MB | 17MB | 9MB | 5MB | 84 | 128 GB | 1025MB | 513MB | 257MB | 129MB | 65MB | 33MB | 17MB | 9MB | 85 | 256 GB | 2049MB | 1025MB | 513MB | 257MB | 129MB | 65MB | 33MB | 17MB | 86 | 512 GB | 4097MB | 2049MB | 1025MB | 513MB | 257MB | 129MB | 65MB | 33MB | 87 | 1024 GB | 8193MB | 4097MB | 2049MB | 1025MB | 513MB | 257MB | 129MB | 65MB | 88 | ``` 89 | 90 | ## Design 91 | 92 | The allocator was designed with the following requirements in mind. 93 | 94 | - Allocation and deallocation operations should behave in a similar and predictable way regardless of the state of the allocator. 95 | - The allocator's metadata size should be predictable based on the arena's size and not dependent on the state of the allocator. 96 | - The allocator's metadata location should be external to the arena. 97 | - Returned memory should be aligned to known and specified block size. 98 | 99 | The following were not design goals 100 | 101 | - To be used by multiple threads at the same time without additional locking. 102 | - To be a general purpose malloc() replacement. 103 | 104 | ## Rationale 105 | 106 | ### Why use a custom allocator (like buddy_alloc) ? 107 | 108 | A custom allocator is useful where there is no system allocator (e.g. on bare-metal) or when the system allocator does not meet some particular requirements, usually in terms of performance or features. The buddy_alloc custom allocator has bounded performance and bounded storage overhead for its metadata. The bounded performance is important in time-sensitive systems that must perform some action in a given amount of time. The bounded storage overhead is important for ensuring system reliability and allows for upfront system resource planing. 109 | 110 | A common example of systems that require both bound performance and bounded storage overhead from their components are games and gaming consoles. Games are time-sensitive in multiple aspects - they have to render frames fast to ensure a smooth display and sample input regularly to account for player input. But just fast is not enough - if an allocator is fast on average but occasionally an operation happens to be an order of magnitude slower this will impact both the display of the game as well as the input and may frustrate the player. Games and game consoles are also sensitive to their storage requirements - game consoles usually ship with fixed hardware and game developers have to optimize their games to perform well on the given machines. 111 | 112 | A custom allocator can supplement the system allocator where needed. A parser that is parsing some structured data (e.g. a json file) may need to allocate objects based on the input's structure. Using the system allocator for this is a risk as the parser may have a bug that causes it to allocate too much or the input may be crafted in such a way. Using a custom allocator with a fixed size for this sort of operations allows the operation to fail safely without impacting the application or the overall system stability. 113 | 114 | An application developer may also need object allocation that is relocatable. Using memory representation as serialization output is a valid technique and it is used for persistence and replication. The buddy_alloc embedded mode is relocatable allowing it to be serialized and restored to a different memory location, a different process or a different machine altogether (provided matching architecture and binaries). 115 | 116 | With the introduction of the `buddy_walk` function the allocator can be used to iterate all the allocated slots with its arena. This can be used for example for a space-bounded mailbox where a failure to allocate means the mailbox is full and the walk can be used to process its content. This can also form the basis of a managed heap for garbage collection. 117 | 118 | ## Implementation 119 | 120 | ``` 121 | +-------------+ +----------------------------------------------------------+ 122 | | | | The allocator component works with 'struct buddy *' and | 123 | | allocator +------------------+ is responsible for the allocator interface (malloc/free )| 124 | | | | and for interfacing with the allocator tree. | 125 | +------+------+ +----------------------------------------------------------+ 126 | | 127 | |(uses) 128 | | 129 | +------v------+ +------------------------------------------------------+ 130 | | | | The allocator tree is the core internal component. | 131 | | allocator +-------------------+ It provides the actual allocation and deallocation | 132 | | tree | | algorithms and uses a binary tree to keep its state. | 133 | | | +------------------------------------------------------+ 134 | +------+------+ 135 | | 136 | |(uses) 137 | | 138 | +------v------+ +---------------------------------------------------+ 139 | | | | The bitset is the allocator tree backing store. | 140 | | bitset +-------------------+ | 141 | | | | The buddy_tree_internal_position_* functions map | 142 | +-------------+ | a tree position to the bitset. | 143 | | | 144 | | The write_to and read_from (internal position) | 145 | | functions encode and decode values in the bitset. | 146 | | | 147 | | Values are encoded in unary with no separators as | 148 | | the struct internal_position lists their length. | 149 | | The unary encoding is faster to encode and decode | 150 | | on unaligned boundaries. | 151 | +---------------------------------------------------+ 152 | ``` 153 | 154 | ### Metadata 155 | 156 | The allocator uses a bitset-backed perfect binary tree to track allocations. The tree is fixed in size and remains outside of the main arena. This allows for better cache performance in the arena as the cache is not loading allocator metadata when processing application data. 157 | 158 | ### Allocation and deallocation 159 | 160 | The binary tree nodes are labeled with the largest allocation slot available under them. This allows allocation to happen with a limited number of operations. Allocations that cannot be satisfied are fast to fail. Once a free node of the desired size is found it is marked as used and the nodes leading to root of the tree are updated to account for any difference in the largest available size. Deallocation works in a similar way - the allocated block size for the given address is found, marked as free and the same node update as with allocation is used to update the tree upwards. 161 | 162 | #### Fragmentation 163 | 164 | To minimize fragmentation the allocator will pick the more heavily-used branches when descending the tree to find a free slot. This ensures that larger continuous spans are kept available for larger-sized allocation requests. A minor benefit is that clumping allocations together can allow for better cache performance. 165 | 166 | ### Space requirements 167 | 168 | The tree is stored in a bitset with each node using just enough bits to store the maximum allocation slot available under it. For leaf nodes this is a single bit. Other nodes sizes depend on the height of the tree. 169 | 170 | ### Non-power-of-two arena sizes 171 | 172 | The perfect binary tree always tracks an arena which size is a power-of-two. When the allocator is initialized or resized with an arena that is not a perfect fit the binary tree is updated to mask out the virtual arena complement to next power-of-two. 173 | 174 | ### Resizing 175 | 176 | Resizing is available for both split and embedded allocator modes and supports both growing the arena and shrinking it. Checks are present that prevent shrinking the arena when memory that is to be reduced is still allocated. 177 | 178 | ## Users 179 | 180 | If you are using buddy_alloc in your project and you would like project to be featured here please send a PR or file an issue. If you like buddy_alloc please star it on GitHub so that more users can learn of it. Thanks! 181 | 182 | - Use in game development - [1](https://github.com/spaskalev/buddy_alloc/issues/13#issue-1088282333), [2](https://github.com/fruityloops1/bf-multiplayer/commit/0c2599390566c7a3f174afc6f3c97b6b3efbeb2c) 183 | - Use in OS kernels - [1](https://github.com/Itay2805/pentagon/blob/1aa005a3f204f40b5869568bd78f4b3087e024a3/kernel/mem/phys.c#L28), [2](https://github.com/spaskalev/buddy_alloc/issues/76), [3](https://github.com/elydre/profanOS/commit/2d43930c36bdc4a5bead2312d7a629e36da4bd78) 184 | - Use in OS research - [1](https://repositories.lib.utexas.edu/server/api/core/bitstreams/ce9f9383-809a-4cc3-ba0b-e8a5e0428ef4/content), [2](https://www.cs.utexas.edu/~witchel/pubs/zhu24dimes-lupin.pdf), [3](https://upcommons.upc.edu/bitstream/handle/2117/411096/main.pdf;jsessionid=8D64ABBCE67117F7F2BB29A51B72CCCF) 185 | - Use in user-space software - [1](https://github.com/liulilittle/PPP-2/commit/6da093802ffa541ea4cf6f92b01ef783d493d706) 186 | - Use in scientific software - [1](https://github.com/ecmwf-ifs/field_api) 187 | -------------------------------------------------------------------------------- /SECURITY.md: -------------------------------------------------------------------------------- 1 | # Security Policy 2 | 3 | Please report all issues via [GitHub's issues page](https://github.com/spaskalev/buddy_alloc/issues). 4 | 5 | If your report contains confidential information you can use GitHub's private reporting of a security vulnerability as documented [here](https://docs.github.com/en/code-security/security-advisories/guidance-on-reporting-and-writing-information-about-vulnerabilities/privately-reporting-a-security-vulnerability). 6 | -------------------------------------------------------------------------------- /_config.yml: -------------------------------------------------------------------------------- 1 | remote_theme: pages-themes/cayman@v0.2.0 2 | plugins: 3 | - jekyll-remote-theme 4 | -------------------------------------------------------------------------------- /bench.c: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2023 Stanislav Paskalev 3 | */ 4 | 5 | #include 6 | #include 7 | #include 8 | #include 9 | #include 10 | #include 11 | 12 | #define BUDDY_ALLOC_ALIGN 64 13 | #ifndef BUDDY_ALLOC_IMPLEMENTATION 14 | #define BUDDY_ALLOC_IMPLEMENTATION 15 | #endif 16 | #include "buddy_alloc.h" 17 | #undef BUDDY_ALLOC_IMPLEMENTATION 18 | 19 | double test_malloc(struct buddy *buddy, size_t alloc_size); 20 | double test_malloc_firstfit(size_t alloc_size); 21 | void *freeing_callback(void *ctx, void *addr, size_t slot_size, size_t allocated); 22 | 23 | int main() { 24 | setvbuf(stdout, NULL, _IONBF, 0); 25 | 26 | size_t arena_size = 1 << 30; 27 | unsigned char *buddy_buf = (unsigned char *) malloc(buddy_sizeof_alignment(arena_size, 64)); 28 | unsigned char *data_buf = (unsigned char *) malloc(arena_size); 29 | struct buddy *buddy = buddy_init_alignment(buddy_buf, data_buf, arena_size, 64); 30 | 31 | double total = 0; 32 | for (size_t i = 0; i <= 6; i++) { 33 | total += test_malloc(buddy, 64 << i); 34 | total += test_malloc(buddy, 64 << i); 35 | total += test_malloc(buddy, 64 << i); 36 | } 37 | printf("Total malloc runtime was %f seconds.\n\n", total); 38 | 39 | free(data_buf); 40 | free(buddy_buf); 41 | } 42 | 43 | double test_malloc(struct buddy *buddy, size_t alloc_size) { 44 | printf("Starting test with alloc size [%zu].\n", alloc_size); 45 | time_t start_time = time(NULL); 46 | 47 | while (buddy_malloc(buddy, alloc_size)) { 48 | // fill it up 49 | } 50 | time_t alloc_time = time(NULL); 51 | 52 | assert(buddy_is_full(buddy)); 53 | 54 | buddy_walk(buddy, freeing_callback, buddy); 55 | assert(buddy_is_empty(buddy)); 56 | 57 | time_t end_time = time(NULL); 58 | double delta = difftime(end_time, start_time); 59 | printf("Test took %.f seconds in total. Allocation: %.f freeing: %.f\n", delta, 60 | difftime(alloc_time, start_time), difftime(end_time, alloc_time)); 61 | 62 | return delta; 63 | } 64 | 65 | void *freeing_callback(void *ctx, void *addr, size_t slot_size, size_t allocated) { 66 | if (! allocated) { 67 | return NULL; 68 | } 69 | struct buddy *buddy = (struct buddy*) ctx; 70 | buddy_free(buddy, addr); 71 | return NULL; 72 | } -------------------------------------------------------------------------------- /buddy_alloc.h: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2021 Stanislav Paskalev 3 | * 4 | * A binary buddy memory allocator 5 | * 6 | * To include and use it in your project do the following 7 | * 1. Add buddy_alloc.h (this file) to your include directory 8 | * 2. Include the header in places where you need to use the allocator 9 | * 3. In one of your source files #define BUDDY_ALLOC_IMPLEMENTATION 10 | * and then import the header. This will insert the implementation. 11 | * 12 | * Latest version is available at https://github.com/spaskalev/buddy_alloc 13 | */ 14 | 15 | #ifndef BUDDY_ALLOC_H 16 | #define BUDDY_ALLOC_H 17 | 18 | #ifndef BUDDY_HEADER 19 | #include 20 | #include 21 | #include 22 | #include 23 | #include 24 | #include 25 | #ifndef BUDDY_PRINTF 26 | #include 27 | #endif 28 | #endif 29 | 30 | #ifdef __cplusplus 31 | #ifndef BUDDY_CPP_MANGLED 32 | extern "C" { 33 | #endif 34 | #endif 35 | 36 | struct buddy; 37 | 38 | /* Returns the size of a buddy required to manage a block of the specified size */ 39 | size_t buddy_sizeof(size_t memory_size); 40 | 41 | /* 42 | * Returns the size of a buddy required to manage a block of the specified size 43 | * using a non-default alignment. 44 | */ 45 | size_t buddy_sizeof_alignment(size_t memory_size, size_t alignment); 46 | 47 | /* Initializes a binary buddy memory allocator at the specified location */ 48 | struct buddy *buddy_init(unsigned char *at, unsigned char *main, size_t memory_size); 49 | 50 | /* Initializes a binary buddy memory allocator at the specified location using a non-default alignment */ 51 | struct buddy *buddy_init_alignment(unsigned char *at, unsigned char *main, size_t memory_size, size_t alignment); 52 | 53 | /* 54 | * Initializes a binary buddy memory allocator embedded in the specified arena. 55 | * The arena's capacity is reduced to account for the allocator metadata. 56 | */ 57 | struct buddy *buddy_embed(unsigned char *main, size_t memory_size); 58 | 59 | /* 60 | * Returns the address of a previously-created buddy allocator at the arena. 61 | * Use to get a new handle to the allocator when the arena is moved or copied. 62 | */ 63 | struct buddy *buddy_get_embed_at(unsigned char *main, size_t memory_size); 64 | 65 | /* 66 | * Initializes a binary buddy memory allocator embedded in the specified arena 67 | * using a non-default alignment. 68 | * The arena's capacity is reduced to account for the allocator metadata. 69 | */ 70 | struct buddy *buddy_embed_alignment(unsigned char *main, size_t memory_size, size_t alignment); 71 | 72 | /* 73 | * Returns the address of a previously-created buddy allocator at the arena. 74 | * Use to get a new handle to the allocator when the arena is moved or copied. 75 | */ 76 | struct buddy *buddy_get_embed_at_alignment(unsigned char *main, size_t memory_size, size_t alignment); 77 | 78 | /* 79 | * Resizes the arena and allocator metadata to a new size. 80 | * 81 | * Existing allocations are preserved. If an allocation is to fall outside 82 | * of the arena after a downsizing the resize operation fails. 83 | * 84 | * Returns a pointer to allocator on successful resize. This will be 85 | * the same pointer when the allocator is external to the arena. If the 86 | * allocator is embedded in the arena the old pointer to the allocator 87 | * must not be used after resizing! 88 | * 89 | * Returns NULL on failure. The allocations and allocator pointer 90 | * are preserved. 91 | */ 92 | struct buddy *buddy_resize(struct buddy *buddy, size_t new_memory_size); 93 | 94 | /* Tests if the arena can be shrunk in half */ 95 | bool buddy_can_shrink(struct buddy *buddy); 96 | 97 | /* Tests if the arena is completely empty */ 98 | bool buddy_is_empty(struct buddy *buddy); 99 | 100 | /* Tests if the arena is completely full */ 101 | bool buddy_is_full(struct buddy *buddy); 102 | 103 | /* Reports the arena size */ 104 | size_t buddy_arena_size(struct buddy *buddy); 105 | 106 | /* Reports the arena's free size. Note that this is (often) not a continuous size 107 | but the sum of all free slots in the buddy. */ 108 | size_t buddy_arena_free_size(struct buddy *buddy); 109 | 110 | /* 111 | * Allocation functions 112 | */ 113 | 114 | /* Use the specified buddy to allocate memory. See malloc. */ 115 | void *buddy_malloc(struct buddy *buddy, size_t requested_size); 116 | 117 | /* Use the specified buddy to allocate zeroed memory. See calloc. */ 118 | void *buddy_calloc(struct buddy *buddy, size_t members_count, size_t member_size); 119 | 120 | /* Realloc semantics are a joke. See realloc. */ 121 | void *buddy_realloc(struct buddy *buddy, void *ptr, size_t requested_size, bool ignore_data); 122 | 123 | /* Realloc-like behavior that checks for overflow. See reallocarray */ 124 | void *buddy_reallocarray(struct buddy *buddy, void *ptr, 125 | size_t members_count, size_t member_size, bool ignore_data); 126 | 127 | /* Use the specified buddy to free memory. See free. */ 128 | void buddy_free(struct buddy *buddy, void *ptr); 129 | 130 | enum buddy_safe_free_status { 131 | BUDDY_SAFE_FREE_SUCCESS, 132 | BUDDY_SAFE_FREE_BUDDY_IS_NULL, 133 | BUDDY_SAFE_FREE_INVALID_ADDRESS, 134 | BUDDY_SAFE_FREE_SIZE_MISMATCH, 135 | BUDDY_SAFE_FREE_ALREADY_FREE, 136 | }; 137 | 138 | /* A (safer) free with a size. Will not free unless the size fits the target span. */ 139 | enum buddy_safe_free_status buddy_safe_free(struct buddy *buddy, void *ptr, size_t requested_size); 140 | 141 | /* 142 | * Reservation functions 143 | */ 144 | 145 | /* Reserve a range by marking it as allocated. Useful for dealing with physical memory. */ 146 | void buddy_reserve_range(struct buddy *buddy, void *ptr, size_t requested_size); 147 | 148 | /* Release a reserved memory range. Unsafe, this can mess up other allocations if called with wrong parameters! */ 149 | void buddy_unsafe_release_range(struct buddy *buddy, void *ptr, size_t requested_size); 150 | 151 | /* 152 | * Iteration functions 153 | */ 154 | 155 | /* 156 | * Iterate through the free and allocated slots and call the provided function for each of them. 157 | * 158 | * If the provided function returns a non-NULL result the iteration stops and the result 159 | * is returned to called. NULL is returned upon completing iteration without stopping. 160 | * 161 | * The iteration order is implementation-defined and may change between versions. 162 | */ 163 | void *buddy_walk(struct buddy *buddy, void *(fp)(void *ctx, void *addr, size_t slot_size, size_t allocated), void *ctx); 164 | 165 | /* 166 | * Miscellaneous functions 167 | */ 168 | 169 | /* 170 | * Calculates the fragmentation in the allocator in a 0 - 255 range. 171 | * NOTE: if you are using a non-power-of-two sized arena the maximum upper bound can be lower. 172 | */ 173 | unsigned char buddy_fragmentation(struct buddy *buddy); 174 | 175 | #ifdef BUDDY_EXPERIMENTAL_CHANGE_TRACKING 176 | /* 177 | * Enable change tracking for this allocator instance. 178 | * 179 | * This will store a header at the start of the arena that contains the function pointer (tracker) and 180 | * a void* (context). The tracker will be called with the context, the start of changed memory and its length. 181 | * 182 | * This function MUST be called before any allocations are performed! 183 | * 184 | * Change tracking is in effect only for allocation functions, resizing functions are excluded from it. 185 | * 186 | * This is an experimental feature designed to facilitate integration with https://github.com/spaskalev/libpvl 187 | * 188 | * The API is not (yet) part of the allocator contract and its semantic versioning! 189 | */ 190 | void buddy_enable_change_tracking(struct buddy* buddy, void* context, void (*tracker) (void*, unsigned char*, size_t)); 191 | #endif 192 | 193 | #ifdef __cplusplus 194 | #ifndef BUDDY_CPP_MANGLED 195 | } 196 | #endif 197 | #endif 198 | 199 | #endif /* BUDDY_ALLOC_H */ 200 | 201 | #ifdef BUDDY_ALLOC_IMPLEMENTATION 202 | #undef BUDDY_ALLOC_IMPLEMENTATION 203 | 204 | #ifdef __cplusplus 205 | #ifndef BUDDY_CPP_MANGLED 206 | extern "C" { 207 | #endif 208 | #endif 209 | 210 | #ifndef BUDDY_ALLOC_ALIGN 211 | #define BUDDY_ALLOC_ALIGN (sizeof(size_t) * CHAR_BIT) 212 | #endif 213 | 214 | #ifdef __cplusplus 215 | #ifndef BUDDY_ALIGNOF 216 | #define BUDDY_ALIGNOF(x) alignof(x) 217 | #endif 218 | 219 | #else /* not __cplusplus */ 220 | 221 | #ifndef BUDDY_ALIGNOF 222 | #ifndef _MSC_VER 223 | #define BUDDY_ALIGNOF(x) __alignof__(x) 224 | #else 225 | #define BUDDY_ALIGNOF(x) _Alignof(x) 226 | #endif 227 | #endif 228 | 229 | #endif /* __cplusplus */ 230 | 231 | /* ssize_t is a POSIX extension */ 232 | #if defined(_MSC_VER) && !defined(_SSIZE_T_DEFINED) 233 | #if _WIN64 234 | typedef signed long long ssize_t; 235 | #else 236 | typedef signed long ssize_t; 237 | #endif 238 | #define _SSIZE_T_DEFINED 239 | #endif 240 | 241 | /* Support compiling with Pelles C */ 242 | #if defined(__POCC__) && defined(__POCC_TARGET__) 243 | #if __POCC_TARGET__ == 3 244 | typedef signed long long ssize_t; 245 | #elif __POCC_TARGET__ == 1 246 | typedef signed long ssize_t; 247 | #else 248 | #error Uknown POCC target 249 | #endif 250 | #endif 251 | 252 | #ifndef BUDDY_PRINTF 253 | #define BUDDY_PRINTF printf 254 | #endif 255 | 256 | /* 257 | * Debug functions 258 | */ 259 | 260 | /* Implementation defined */ 261 | void buddy_debug(struct buddy *buddy); 262 | 263 | struct buddy_change_tracker { 264 | void* context; 265 | void (*tracker) (void*, unsigned char*, size_t); 266 | }; 267 | 268 | struct buddy_tree; 269 | 270 | struct buddy_tree_pos { 271 | size_t index; 272 | size_t depth; 273 | }; 274 | 275 | #ifdef __cplusplus 276 | #define INVALID_POS buddy_tree_pos{ 0, 0 } 277 | #else 278 | #define INVALID_POS ((struct buddy_tree_pos){ 0, 0 }) 279 | #endif 280 | 281 | struct buddy_tree_interval { 282 | struct buddy_tree_pos from; 283 | struct buddy_tree_pos to; 284 | }; 285 | 286 | struct buddy_tree_walk_state { 287 | struct buddy_tree_pos starting_pos; 288 | struct buddy_tree_pos current_pos; 289 | unsigned int going_up; 290 | unsigned int walk_done; 291 | }; 292 | 293 | /* 294 | * Initialization functions 295 | */ 296 | 297 | /* Returns the size of a buddy allocation tree of the desired order*/ 298 | static size_t buddy_tree_sizeof(uint8_t order); 299 | 300 | /* Initializes a buddy allocation tree at the specified location */ 301 | static struct buddy_tree *buddy_tree_init(unsigned char *at, uint8_t order); 302 | 303 | /* Indicates whether this is a valid position for the tree */ 304 | static bool buddy_tree_valid(struct buddy_tree *t, struct buddy_tree_pos pos); 305 | 306 | /* Returns the order of the specified buddy allocation tree */ 307 | static uint8_t buddy_tree_order(struct buddy_tree *t); 308 | 309 | /* 310 | * Resize the tree to the new order. When downsizing the left subtree is picked. 311 | * Caller must ensure enough space for the new order. 312 | */ 313 | static void buddy_tree_resize(struct buddy_tree *t, uint8_t desired_order); 314 | 315 | #ifdef BUDDY_EXPERIMENTAL_CHANGE_TRACKING 316 | /* Enable change tracking state for this tree. */ 317 | static void buddy_tree_enable_change_tracking(struct buddy_tree *t); 318 | #endif /* BUDDY_EXPERIMENTAL_CHANGE_TRACKING */ 319 | 320 | /* 321 | * Navigation functions 322 | */ 323 | 324 | /* Returns a position at the root of a buddy allocation tree */ 325 | static struct buddy_tree_pos buddy_tree_root(void); 326 | 327 | /* Returns the leftmost child node */ 328 | static struct buddy_tree_pos buddy_tree_leftmost_child(struct buddy_tree *t); 329 | 330 | /* Returns the tree depth of the indicated position */ 331 | static inline size_t buddy_tree_depth(struct buddy_tree_pos pos); 332 | 333 | /* Returns the left child node position. Does not check if that is a valid position */ 334 | static inline struct buddy_tree_pos buddy_tree_left_child(struct buddy_tree_pos pos); 335 | 336 | /* Returns the right child node position. Does not check if that is a valid position */ 337 | static inline struct buddy_tree_pos buddy_tree_right_child(struct buddy_tree_pos pos); 338 | 339 | /* Returns the current sibling node position. Does not check if that is a valid position */ 340 | static inline struct buddy_tree_pos buddy_tree_sibling(struct buddy_tree_pos pos); 341 | 342 | /* Returns the parent node position or an invalid position if there is no parent node */ 343 | static inline struct buddy_tree_pos buddy_tree_parent(struct buddy_tree_pos pos); 344 | 345 | /* Returns the right adjacent node position or an invalid position if there is no right adjacent node */ 346 | static struct buddy_tree_pos buddy_tree_right_adjacent(struct buddy_tree_pos pos); 347 | 348 | /* Returns the at-depth index of the indicated position */ 349 | static size_t buddy_tree_index(struct buddy_tree_pos pos); 350 | 351 | /* Return the interval of the deepest positions spanning the indicated position */ 352 | static struct buddy_tree_interval buddy_tree_interval(struct buddy_tree *t, struct buddy_tree_pos pos); 353 | 354 | /* Checks if one interval contains another */ 355 | static bool buddy_tree_interval_contains(struct buddy_tree_interval outer, 356 | struct buddy_tree_interval inner); 357 | 358 | /* Return a walk state structure starting from the root of a tree */ 359 | static struct buddy_tree_walk_state buddy_tree_walk_state_root(void); 360 | 361 | /* Walk the tree, keeping track in the provided state structure */ 362 | static unsigned int buddy_tree_walk(struct buddy_tree *t, struct buddy_tree_walk_state *state); 363 | 364 | 365 | /* 366 | * Allocation functions 367 | */ 368 | 369 | /* Returns the free capacity at or underneath the indicated position */ 370 | static size_t buddy_tree_status(struct buddy_tree *t, struct buddy_tree_pos pos); 371 | 372 | /* Marks the indicated position as allocated and propagates the change */ 373 | static void buddy_tree_mark(struct buddy_tree *t, struct buddy_tree_pos pos); 374 | 375 | enum buddy_tree_release_status { 376 | BUDDY_TREE_RELEASE_SUCCESS, 377 | BUDDY_TREE_RELEASE_FAIL_PARTIALLY_USED, 378 | }; 379 | 380 | /* Marks the indicated position as free and propagates the change */ 381 | static enum buddy_tree_release_status buddy_tree_release(struct buddy_tree *t, struct buddy_tree_pos pos); 382 | 383 | /* Returns a free position at the specified depth or an invalid position */ 384 | static struct buddy_tree_pos buddy_tree_find_free(struct buddy_tree *t, uint8_t depth); 385 | 386 | /* Tests if the indicated position is available for allocation */ 387 | static bool buddy_tree_is_free(struct buddy_tree *t, struct buddy_tree_pos pos); 388 | 389 | /* Tests if the tree can be shrank in half */ 390 | static bool buddy_tree_can_shrink(struct buddy_tree *t); 391 | 392 | /* 393 | * Integration functions 394 | */ 395 | 396 | #ifdef BUDDY_EXPERIMENTAL_CHANGE_TRACKING 397 | /* Get a pointer to the parent buddy struct */ 398 | static struct buddy* buddy_tree_buddy(struct buddy_tree* t); 399 | #endif /* BUDDY_EXPERIMENTAL_CHANGE_TRACKING */ 400 | 401 | /* 402 | * Debug functions 403 | */ 404 | 405 | /* Implementation defined */ 406 | static void buddy_tree_debug(struct buddy_tree *t, struct buddy_tree_pos pos, size_t start_size); 407 | 408 | /* Implementation defined */ 409 | unsigned int buddy_tree_check_invariant(struct buddy_tree *t, struct buddy_tree_pos pos); 410 | 411 | /* Report fragmentation in a 0 - 255 range */ 412 | static unsigned char buddy_tree_fragmentation(struct buddy_tree *t); 413 | 414 | /* 415 | * A char-backed bitset implementation 416 | */ 417 | 418 | static size_t bitset_sizeof(size_t elements); 419 | 420 | struct bitset_range { 421 | size_t from_bucket; 422 | size_t to_bucket; 423 | 424 | uint8_t from_index; 425 | uint8_t to_index; 426 | }; 427 | 428 | static inline struct bitset_range bitset_range(size_t from_pos, size_t to_pos); 429 | 430 | static void bitset_set_range(unsigned char *bitset, struct bitset_range range); 431 | 432 | static void bitset_clear_range(unsigned char *bitset, struct bitset_range range); 433 | 434 | static size_t bitset_count_range(unsigned char *bitset, struct bitset_range range); 435 | 436 | static inline void bitset_set(unsigned char *bitset, size_t pos); 437 | 438 | static inline void bitset_clear(unsigned char *bitset, size_t pos); 439 | 440 | static inline bool bitset_test(const unsigned char *bitset, size_t pos); 441 | 442 | static void bitset_shift_left(unsigned char *bitset, size_t from_pos, size_t to_pos, size_t by); 443 | 444 | static void bitset_shift_right(unsigned char *bitset, size_t from_pos, size_t to_pos, size_t by); 445 | 446 | /* 447 | * Debug functions 448 | */ 449 | 450 | /* Implementation defined */ 451 | void bitset_debug(unsigned char *bitset, size_t length); 452 | 453 | /* 454 | * Bits 455 | */ 456 | 457 | /* Returns the number of set bits in the given byte */ 458 | static unsigned int popcount_byte(unsigned char b); 459 | 460 | /* Returns the index of the highest bit set (1-based) */ 461 | static size_t highest_bit_position(size_t value); 462 | 463 | /* Returns the nearest larger or equal power of two */ 464 | static inline size_t ceiling_power_of_two(size_t value); 465 | 466 | /* Return two to the power of order */ 467 | static inline size_t two_to_the_power_of(size_t order); 468 | 469 | /* 470 | * Math 471 | */ 472 | 473 | /* Calculates the integer square root of an integer */ 474 | static inline size_t integer_square_root(size_t f); 475 | 476 | /* 477 | Implementation 478 | */ 479 | 480 | const unsigned int BUDDY_RELATIVE_MODE = 1; 481 | 482 | /* 483 | * A binary buddy memory allocator 484 | */ 485 | 486 | struct buddy { 487 | size_t memory_size; 488 | size_t alignment; 489 | union { 490 | unsigned char *main; 491 | ptrdiff_t main_offset; 492 | } arena; 493 | size_t buddy_flags; 494 | }; 495 | 496 | struct buddy_embed_check { 497 | unsigned int can_fit; 498 | size_t offset; 499 | size_t buddy_size; 500 | }; 501 | 502 | static unsigned int is_valid_alignment(size_t alignment); 503 | static size_t buddy_tree_order_for_memory(size_t memory_size, size_t alignment); 504 | static size_t depth_for_size(struct buddy *buddy, size_t requested_size); 505 | static inline size_t size_for_depth(struct buddy *buddy, size_t depth); 506 | static unsigned char *address_for_position(struct buddy *buddy, struct buddy_tree_pos pos); 507 | static struct buddy_tree_pos position_for_address(struct buddy *buddy, const unsigned char *addr); 508 | static unsigned char *buddy_main(struct buddy *buddy); 509 | static unsigned int buddy_relative_mode(struct buddy *buddy); 510 | static struct buddy_tree *buddy_tree(struct buddy *buddy); 511 | static size_t buddy_effective_memory_size(struct buddy *buddy); 512 | static size_t buddy_virtual_slots(struct buddy *buddy); 513 | static void buddy_toggle_virtual_slots(struct buddy *buddy, unsigned int state); 514 | static void buddy_toggle_range_reservation(struct buddy *buddy, void *ptr, size_t requested_size, unsigned int state); 515 | static struct buddy *buddy_resize_standard(struct buddy *buddy, size_t new_memory_size); 516 | static struct buddy *buddy_resize_embedded(struct buddy *buddy, size_t new_memory_size); 517 | static bool buddy_is_free(struct buddy *buddy, size_t from); 518 | static struct buddy_embed_check buddy_embed_offset(size_t memory_size, size_t alignment); 519 | static struct buddy_tree_pos deepest_position_for_offset(struct buddy *buddy, size_t offset); 520 | 521 | size_t buddy_sizeof(size_t memory_size) { 522 | return buddy_sizeof_alignment(memory_size, BUDDY_ALLOC_ALIGN); 523 | } 524 | 525 | size_t buddy_sizeof_alignment(size_t memory_size, size_t alignment) { 526 | size_t buddy_tree_order; 527 | 528 | if (!is_valid_alignment(alignment)) { 529 | return 0; /* invalid */ 530 | } 531 | if (memory_size < alignment) { 532 | return 0; /* invalid */ 533 | } 534 | buddy_tree_order = buddy_tree_order_for_memory(memory_size, alignment); 535 | return sizeof(struct buddy) + buddy_tree_sizeof((uint8_t)buddy_tree_order); 536 | } 537 | 538 | struct buddy *buddy_init(unsigned char *at, unsigned char *main, size_t memory_size) { 539 | return buddy_init_alignment(at, main, memory_size, BUDDY_ALLOC_ALIGN); 540 | } 541 | 542 | struct buddy *buddy_init_alignment(unsigned char *at, unsigned char *main, size_t memory_size, 543 | size_t alignment) { 544 | size_t at_alignment, main_alignment, buddy_size, buddy_tree_order; 545 | struct buddy *buddy; 546 | 547 | if (at == NULL) { 548 | return NULL; 549 | } 550 | if (main == NULL) { 551 | return NULL; 552 | } 553 | if (at == main) { 554 | return NULL; 555 | } 556 | if (!is_valid_alignment(alignment)) { 557 | return NULL; /* invalid */ 558 | } 559 | at_alignment = ((uintptr_t) at) % BUDDY_ALIGNOF(struct buddy); 560 | if (at_alignment != 0) { 561 | return NULL; 562 | } 563 | main_alignment = ((uintptr_t) main) % BUDDY_ALIGNOF(size_t); 564 | if (main_alignment != 0) { 565 | return NULL; 566 | } 567 | /* Trim down memory to alignment */ 568 | if (memory_size % alignment) { 569 | memory_size -= (memory_size % alignment); 570 | } 571 | buddy_size = buddy_sizeof_alignment(memory_size, alignment); 572 | if (buddy_size == 0) { 573 | return NULL; 574 | } 575 | buddy_tree_order = buddy_tree_order_for_memory(memory_size, alignment); 576 | 577 | /* TODO check for overlap between buddy metadata and main block */ 578 | buddy = (struct buddy *) at; 579 | buddy->arena.main = main; 580 | buddy->memory_size = memory_size; 581 | buddy->buddy_flags = 0; 582 | buddy->alignment = alignment; 583 | buddy_tree_init((unsigned char *)buddy + sizeof(*buddy), (uint8_t) buddy_tree_order); 584 | buddy_toggle_virtual_slots(buddy, 1); 585 | return buddy; 586 | } 587 | 588 | struct buddy *buddy_embed(unsigned char *main, size_t memory_size) { 589 | return buddy_embed_alignment(main, memory_size, BUDDY_ALLOC_ALIGN); 590 | } 591 | 592 | struct buddy *buddy_get_embed_at(unsigned char *main, size_t memory_size) { 593 | return buddy_get_embed_at_alignment(main, memory_size, BUDDY_ALLOC_ALIGN); 594 | } 595 | 596 | struct buddy *buddy_embed_alignment(unsigned char *main, size_t memory_size, size_t alignment) { 597 | struct buddy_embed_check check_result; 598 | struct buddy *buddy; 599 | 600 | if (! main) { 601 | return NULL; 602 | } 603 | if (!is_valid_alignment(alignment)) { 604 | return NULL; /* invalid */ 605 | } 606 | check_result = buddy_embed_offset(memory_size, alignment); 607 | if (! check_result.can_fit) { 608 | return NULL; 609 | } 610 | 611 | buddy = buddy_init_alignment(main+check_result.offset, main, check_result.offset, alignment); 612 | if (! buddy) { /* regular initialization failed */ 613 | return NULL; 614 | } 615 | 616 | buddy->buddy_flags |= BUDDY_RELATIVE_MODE; 617 | buddy->arena.main_offset = (unsigned char *)buddy - main; 618 | return buddy; 619 | } 620 | 621 | struct buddy *buddy_get_embed_at_alignment(unsigned char *main, size_t memory_size, size_t alignment) { 622 | struct buddy_embed_check check_result = buddy_embed_offset(memory_size, alignment); 623 | if (!check_result.can_fit) { 624 | return NULL; 625 | } 626 | return (struct buddy *)(main + check_result.offset); 627 | } 628 | 629 | struct buddy *buddy_resize(struct buddy *buddy, size_t new_memory_size) { 630 | if (new_memory_size == buddy->memory_size) { 631 | return buddy; 632 | } 633 | 634 | if (buddy_relative_mode(buddy)) { 635 | return buddy_resize_embedded(buddy, new_memory_size); 636 | } else { 637 | return buddy_resize_standard(buddy, new_memory_size); 638 | } 639 | } 640 | 641 | static struct buddy *buddy_resize_standard(struct buddy *buddy, size_t new_memory_size) { 642 | size_t new_buddy_tree_order; 643 | 644 | /* Trim down memory to alignment */ 645 | if (new_memory_size % buddy->alignment) { 646 | new_memory_size -= (new_memory_size % buddy->alignment); 647 | } 648 | 649 | /* Account for tree use */ 650 | if (!buddy_is_free(buddy, new_memory_size)) { 651 | return NULL; 652 | } 653 | 654 | /* Release the virtual slots */ 655 | buddy_toggle_virtual_slots(buddy, 0); 656 | 657 | /* Calculate new tree order and resize it */ 658 | new_buddy_tree_order = buddy_tree_order_for_memory(new_memory_size, buddy->alignment); 659 | buddy_tree_resize(buddy_tree(buddy), (uint8_t) new_buddy_tree_order); 660 | 661 | /* Store the new memory size and reconstruct any virtual slots */ 662 | buddy->memory_size = new_memory_size; 663 | buddy_toggle_virtual_slots(buddy, 1); 664 | 665 | /* Resize successful */ 666 | return buddy; 667 | } 668 | 669 | static struct buddy *buddy_resize_embedded(struct buddy *buddy, size_t new_memory_size) { 670 | struct buddy_embed_check check_result; 671 | unsigned char *main, *buddy_destination; 672 | struct buddy *resized, *relocated; 673 | 674 | /* Ensure that the embedded allocator can fit */ 675 | check_result = buddy_embed_offset(new_memory_size, buddy->alignment); 676 | if (! check_result.can_fit) { 677 | return NULL; 678 | } 679 | 680 | /* Resize the allocator in the normal way */ 681 | resized = buddy_resize_standard(buddy, check_result.offset); 682 | if (! resized) { 683 | return NULL; 684 | } 685 | 686 | /* Get the absolute main address. The relative will be invalid after relocation. */ 687 | main = buddy_main(buddy); 688 | 689 | /* Relocate the allocator */ 690 | buddy_destination = buddy_main(buddy) + check_result.offset; 691 | memmove(buddy_destination, resized, check_result.buddy_size); 692 | 693 | /* Update the main offset in the allocator */ 694 | relocated = (struct buddy *) buddy_destination; 695 | relocated->arena.main_offset = buddy_destination - main; 696 | 697 | return relocated; 698 | } 699 | 700 | bool buddy_can_shrink(struct buddy *buddy) { 701 | if (buddy == NULL) { 702 | return false; 703 | } 704 | return buddy_is_free(buddy, buddy->memory_size / 2); 705 | } 706 | 707 | bool buddy_is_empty(struct buddy *buddy) { 708 | if (buddy == NULL) { 709 | return false; 710 | } 711 | return buddy_is_free(buddy, 0); 712 | } 713 | 714 | bool buddy_is_full(struct buddy *buddy) { 715 | struct buddy_tree *tree; 716 | struct buddy_tree_pos pos; 717 | 718 | if (buddy == NULL) { 719 | return false; 720 | } 721 | tree = buddy_tree(buddy); 722 | pos = buddy_tree_root(); 723 | return buddy_tree_status(tree, pos) == buddy_tree_order(tree); 724 | } 725 | 726 | size_t buddy_arena_size(struct buddy *buddy) { 727 | if (buddy == NULL) { 728 | return 0; 729 | } 730 | return buddy->memory_size; 731 | } 732 | 733 | size_t buddy_arena_free_size(struct buddy *buddy) { 734 | size_t result = 0; 735 | struct buddy_tree *tree = buddy_tree(buddy); 736 | size_t tree_order = buddy_tree_order(tree); 737 | 738 | struct buddy_tree_walk_state state = buddy_tree_walk_state_root(); 739 | do { 740 | size_t pos_status = buddy_tree_status(tree, state.current_pos); 741 | if (pos_status == (tree_order - state.current_pos.depth + 1)) { /* Fully-allocated */ 742 | state.going_up = 1; 743 | } else if (pos_status == 0) { /* Free */ 744 | state.going_up = 1; 745 | result += size_for_depth(buddy, state.current_pos.depth); 746 | } else { /* Partial */ 747 | continue; 748 | } 749 | } while (buddy_tree_walk(tree, &state)); 750 | return result; 751 | } 752 | 753 | static unsigned int is_valid_alignment(size_t alignment) { 754 | return ceiling_power_of_two(alignment) == alignment; 755 | } 756 | 757 | static size_t buddy_tree_order_for_memory(size_t memory_size, size_t alignment) { 758 | size_t blocks = memory_size / alignment; 759 | return highest_bit_position(ceiling_power_of_two(blocks)); 760 | } 761 | 762 | void *buddy_malloc(struct buddy *buddy, size_t requested_size) { 763 | size_t target_depth; 764 | struct buddy_tree *tree; 765 | struct buddy_tree_pos pos; 766 | 767 | if (buddy == NULL) { 768 | return NULL; 769 | } 770 | if (requested_size == 0) { 771 | /* 772 | * Batshit crazy code exists that calls malloc(0) and expects 773 | * a result that can be safely passed to free(). 774 | * And even though this allocator will safely handle a free(NULL) 775 | * the particular batshit code will expect a non-NULL malloc(0) result! 776 | * 777 | * See also https://wiki.sei.cmu.edu/confluence/display/c/MEM04-C.+Beware+of+zero-length+allocations 778 | */ 779 | requested_size = 1; 780 | } 781 | if (requested_size > buddy->memory_size) { 782 | return NULL; 783 | } 784 | 785 | target_depth = depth_for_size(buddy, requested_size); 786 | tree = buddy_tree(buddy); 787 | pos = buddy_tree_find_free(tree, (uint8_t) target_depth); 788 | 789 | if (! buddy_tree_valid(tree, pos)) { 790 | return NULL; /* no slot found */ 791 | } 792 | 793 | /* Allocate the slot */ 794 | buddy_tree_mark(tree, pos); 795 | 796 | /* Find and return the actual memory address */ 797 | return address_for_position(buddy, pos); 798 | } 799 | 800 | void *buddy_calloc(struct buddy *buddy, size_t members_count, size_t member_size) { 801 | size_t total_size; 802 | void *result; 803 | 804 | if (members_count == 0 || member_size == 0) { 805 | /* See the gleeful remark in malloc */ 806 | members_count = 1; 807 | member_size = 1; 808 | } 809 | /* Check for overflow */ 810 | if (((members_count * member_size)/members_count) != member_size) { 811 | return NULL; 812 | } 813 | total_size = members_count * member_size; 814 | result = buddy_malloc(buddy, total_size); 815 | if (result) { 816 | memset(result, 0, total_size); 817 | } 818 | return result; 819 | } 820 | 821 | void *buddy_realloc(struct buddy *buddy, void *ptr, size_t requested_size, bool ignore_data) { 822 | struct buddy_tree *tree; 823 | struct buddy_tree_pos origin, new_pos; 824 | size_t current_depth, target_depth; 825 | void *source, *destination; 826 | 827 | /* 828 | * realloc is a joke: 829 | * - NULL ptr degrades into malloc 830 | * - Zero size degrades into free 831 | * - Same size as previous malloc/calloc/realloc is a no-op or a rellocation 832 | * - Smaller size than previous *alloc decrease the allocated size with an optional rellocation 833 | * - If the new allocation cannot be satisfied NULL is returned BUT the slot is preserved 834 | * - Larger size than previous *alloc increase tha allocated size with an optional rellocation 835 | */ 836 | if (ptr == NULL) { 837 | return buddy_malloc(buddy, requested_size); 838 | } 839 | if (requested_size == 0) { 840 | buddy_free(buddy, ptr); 841 | return NULL; 842 | } 843 | if (requested_size > buddy->memory_size) { 844 | return NULL; 845 | } 846 | 847 | /* Find the position tracking this address */ 848 | tree = buddy_tree(buddy); 849 | origin = position_for_address(buddy, (unsigned char *) ptr); 850 | if (! buddy_tree_valid(tree, origin)) { 851 | return NULL; 852 | } 853 | current_depth = buddy_tree_depth(origin); 854 | target_depth = depth_for_size(buddy, requested_size); 855 | 856 | /* Release the position and perform a search */ 857 | buddy_tree_release(tree, origin); 858 | new_pos = buddy_tree_find_free(tree, (uint8_t) target_depth); 859 | 860 | if (! buddy_tree_valid(tree, new_pos)) { 861 | /* allocation failure, restore mark and return null */ 862 | buddy_tree_mark(tree, origin); 863 | return NULL; 864 | } 865 | 866 | if (origin.index == new_pos.index) { 867 | /* Allocated to the same slot, restore mark and return null */ 868 | buddy_tree_mark(tree, origin); 869 | return ptr; 870 | } 871 | 872 | destination = address_for_position(buddy, new_pos); 873 | 874 | if (! ignore_data) { 875 | /* Copy the content */ 876 | source = address_for_position(buddy, origin); 877 | memmove(destination, source, size_for_depth(buddy, 878 | current_depth > target_depth ? current_depth : target_depth)); 879 | } 880 | 881 | /* Allocate and return */ 882 | buddy_tree_mark(tree, new_pos); 883 | return destination; 884 | } 885 | 886 | void *buddy_reallocarray(struct buddy *buddy, void *ptr, 887 | size_t members_count, size_t member_size, bool ignore_data) { 888 | if (members_count == 0 || member_size == 0) { 889 | return buddy_realloc(buddy, ptr, 0, ignore_data); 890 | } 891 | /* Check for overflow */ 892 | if ((members_count * member_size)/members_count != member_size) { 893 | return NULL; 894 | } 895 | return buddy_realloc(buddy, ptr, members_count * member_size, ignore_data); 896 | } 897 | 898 | void buddy_free(struct buddy *buddy, void *ptr) { 899 | unsigned char *dst, *main; 900 | struct buddy_tree *tree; 901 | struct buddy_tree_pos pos; 902 | 903 | if (buddy == NULL) { 904 | return; 905 | } 906 | if (ptr == NULL) { 907 | return; 908 | } 909 | dst = (unsigned char *)ptr; 910 | main = buddy_main(buddy); 911 | if ((dst < main) || (dst >= (main + buddy->memory_size))) { 912 | return; 913 | } 914 | 915 | /* Find the position tracking this address */ 916 | tree = buddy_tree(buddy); 917 | pos = position_for_address(buddy, dst); 918 | 919 | if (! buddy_tree_valid(tree, pos)) { 920 | return; 921 | } 922 | 923 | /* Release the position */ 924 | buddy_tree_release(tree, pos); 925 | } 926 | 927 | enum buddy_safe_free_status buddy_safe_free(struct buddy* buddy, void* ptr, size_t requested_size) { 928 | unsigned char* dst, * main; 929 | struct buddy_tree* tree; 930 | struct buddy_tree_pos pos; 931 | size_t allocated_size_for_depth; 932 | enum buddy_tree_release_status status; 933 | 934 | if (buddy == NULL) { 935 | return BUDDY_SAFE_FREE_BUDDY_IS_NULL; 936 | } 937 | if (ptr == NULL) { 938 | return BUDDY_SAFE_FREE_INVALID_ADDRESS; 939 | } 940 | dst = (unsigned char*)ptr; 941 | main = buddy_main(buddy); 942 | if ((dst < main) || (dst >= (main + buddy->memory_size))) { 943 | return BUDDY_SAFE_FREE_INVALID_ADDRESS; 944 | } 945 | 946 | /* Find an allocated position tracking this address */ 947 | tree = buddy_tree(buddy); 948 | pos = position_for_address(buddy, dst); 949 | 950 | if (!buddy_tree_valid(tree, pos)) { 951 | return BUDDY_SAFE_FREE_INVALID_ADDRESS; 952 | } 953 | 954 | allocated_size_for_depth = size_for_depth(buddy, pos.depth); 955 | if (requested_size < buddy->alignment) { 956 | requested_size = buddy->alignment; 957 | } 958 | if (requested_size > allocated_size_for_depth) { 959 | return BUDDY_SAFE_FREE_SIZE_MISMATCH; 960 | } 961 | if (requested_size <= (allocated_size_for_depth / 2)) { 962 | return BUDDY_SAFE_FREE_SIZE_MISMATCH; 963 | } 964 | 965 | /* Release the position */ 966 | status = buddy_tree_release(tree, pos); 967 | 968 | switch (status) { 969 | case BUDDY_TREE_RELEASE_FAIL_PARTIALLY_USED: 970 | return BUDDY_SAFE_FREE_INVALID_ADDRESS; 971 | case BUDDY_TREE_RELEASE_SUCCESS: 972 | break; 973 | } 974 | 975 | return BUDDY_SAFE_FREE_SUCCESS; 976 | } 977 | 978 | void buddy_reserve_range(struct buddy *buddy, void *ptr, size_t requested_size) { 979 | buddy_toggle_range_reservation(buddy, ptr, requested_size, 1); 980 | } 981 | 982 | void buddy_unsafe_release_range(struct buddy *buddy, void *ptr, size_t requested_size) { 983 | buddy_toggle_range_reservation(buddy, ptr, requested_size, 0); 984 | } 985 | 986 | void *buddy_walk(struct buddy *buddy, 987 | void *(fp)(void *ctx, void *addr, size_t slot_size, size_t allocated), 988 | void *ctx) { 989 | unsigned char *main; 990 | size_t effective_memory_size, tree_order, pos_status, pos_size; 991 | struct buddy_tree *tree; 992 | unsigned char *addr; 993 | struct buddy_tree_walk_state state; 994 | struct buddy_tree_pos test_pos; 995 | void *callback_result; 996 | 997 | if (buddy == NULL) { 998 | return NULL; 999 | } 1000 | if (fp == NULL) { 1001 | return NULL; 1002 | } 1003 | main = buddy_main(buddy); 1004 | effective_memory_size = buddy_effective_memory_size(buddy); 1005 | tree = buddy_tree(buddy); 1006 | tree_order = buddy_tree_order(tree); 1007 | 1008 | state = buddy_tree_walk_state_root(); 1009 | do { 1010 | pos_status = buddy_tree_status(tree, state.current_pos); 1011 | if (pos_status != (tree_order - state.current_pos.depth + 1)) { /* Partially-allocated */ 1012 | continue; 1013 | } 1014 | 1015 | /* 1016 | * The tree doesn't make a distinction of a fully-allocated node 1017 | * due to a single allocation and a fully-allocated due to maxed out 1018 | * child allocations - we need to check the children. 1019 | * A child-allocated node will have both children set to their maximum 1020 | * but it is sufficient to check just one for non-zero. 1021 | */ 1022 | test_pos = buddy_tree_left_child(state.current_pos); 1023 | if (buddy_tree_valid(tree, test_pos) && buddy_tree_status(tree, test_pos)) { 1024 | continue; 1025 | } 1026 | 1027 | /* Current node is free or allocated, process */ 1028 | pos_size = effective_memory_size >> (state.current_pos.depth - 1u); 1029 | addr = address_for_position(buddy, state.current_pos); 1030 | if (((size_t)(addr - main) + pos_size) > buddy->memory_size) { 1031 | /* 1032 | * Do not process virtual slots 1033 | * As virtual slots are on the right side of the tree 1034 | * if we see a one with the current iteration order this 1035 | * means that all subsequent slots will be virtual, 1036 | * hence we can return early. 1037 | */ 1038 | return NULL; 1039 | } 1040 | callback_result = (fp)(ctx, addr, pos_size, pos_status > 0); 1041 | if (callback_result != NULL) { 1042 | return callback_result; 1043 | } 1044 | state.going_up = 1; 1045 | 1046 | } while (buddy_tree_walk(tree, &state)); 1047 | return NULL; 1048 | } 1049 | 1050 | unsigned char buddy_fragmentation(struct buddy *buddy) { 1051 | if (buddy == NULL) { 1052 | return 0; 1053 | } 1054 | return buddy_tree_fragmentation(buddy_tree(buddy)); 1055 | } 1056 | 1057 | #ifdef BUDDY_EXPERIMENTAL_CHANGE_TRACKING 1058 | void buddy_enable_change_tracking(struct buddy* buddy, void* context, void (*tracker) (void*, unsigned char*, size_t)) { 1059 | struct buddy_tree *t = buddy_tree(buddy); 1060 | struct buddy_change_tracker *header = (struct buddy_change_tracker *) buddy_main(buddy); 1061 | 1062 | /* Allocate memory for the change tracking header */ 1063 | buddy_reserve_range(buddy, buddy_main(buddy), sizeof(struct buddy_change_tracker)); 1064 | 1065 | /* Fill in the change tracking header */ 1066 | header->context = context; 1067 | header->tracker = tracker; 1068 | 1069 | /* Indicate that the tree should perform change tracking */ 1070 | buddy_tree_enable_change_tracking(t); 1071 | } 1072 | #endif 1073 | 1074 | 1075 | static size_t depth_for_size(struct buddy *buddy, size_t requested_size) { 1076 | size_t depth, effective_memory_size; 1077 | if (requested_size < buddy->alignment) { 1078 | requested_size = buddy->alignment; 1079 | } 1080 | depth = 1; 1081 | effective_memory_size = buddy_effective_memory_size(buddy); 1082 | while ((effective_memory_size / requested_size) >> 1u) { 1083 | depth++; 1084 | effective_memory_size >>= 1u; 1085 | } 1086 | return depth; 1087 | } 1088 | 1089 | static inline size_t size_for_depth(struct buddy *buddy, size_t depth) { 1090 | return ceiling_power_of_two(buddy->memory_size) >> (depth-1); 1091 | } 1092 | 1093 | static struct buddy_tree *buddy_tree(struct buddy *buddy) { 1094 | return (struct buddy_tree*) ((unsigned char *)buddy + sizeof(*buddy)); 1095 | } 1096 | 1097 | static size_t buddy_effective_memory_size(struct buddy *buddy) { 1098 | return ceiling_power_of_two(buddy->memory_size); 1099 | } 1100 | 1101 | static size_t buddy_virtual_slots(struct buddy *buddy) { 1102 | size_t memory_size = buddy->memory_size; 1103 | size_t effective_memory_size = buddy_effective_memory_size(buddy); 1104 | if (effective_memory_size == memory_size) { 1105 | return 0; 1106 | } 1107 | return (effective_memory_size - memory_size) / buddy->alignment; 1108 | } 1109 | 1110 | static unsigned char *address_for_position(struct buddy *buddy, struct buddy_tree_pos pos) { 1111 | size_t block_size = size_for_depth(buddy, buddy_tree_depth(pos)); 1112 | size_t addr = block_size * buddy_tree_index(pos); 1113 | return buddy_main(buddy) + addr; 1114 | } 1115 | 1116 | static struct buddy_tree_pos deepest_position_for_offset(struct buddy *buddy, size_t offset) { 1117 | size_t index = offset / buddy->alignment; 1118 | struct buddy_tree_pos pos = buddy_tree_leftmost_child(buddy_tree(buddy)); 1119 | pos.index += index; 1120 | return pos; 1121 | } 1122 | 1123 | static struct buddy_tree_pos position_for_address(struct buddy *buddy, const unsigned char *addr) { 1124 | unsigned char *main; 1125 | struct buddy_tree *tree; 1126 | struct buddy_tree_pos pos; 1127 | size_t offset; 1128 | 1129 | main = buddy_main(buddy); 1130 | offset = (size_t) (addr - main); 1131 | 1132 | if (offset % buddy->alignment) { 1133 | return INVALID_POS; /* invalid alignment */ 1134 | } 1135 | 1136 | tree = buddy_tree(buddy); 1137 | pos = deepest_position_for_offset(buddy, offset); 1138 | 1139 | /* Find the actual allocated position tracking this address */ 1140 | while (!buddy_tree_status(tree, pos)) { 1141 | pos = buddy_tree_parent(pos); 1142 | 1143 | if (!buddy_tree_valid(tree, pos)) { 1144 | return INVALID_POS; 1145 | } 1146 | } 1147 | 1148 | if (address_for_position(buddy, pos) != addr) { 1149 | return INVALID_POS; /* invalid alignment */ 1150 | } 1151 | 1152 | return pos; 1153 | } 1154 | 1155 | static unsigned char *buddy_main(struct buddy *buddy) { 1156 | if (buddy_relative_mode(buddy)) { 1157 | return (unsigned char *)buddy - buddy->arena.main_offset; 1158 | } 1159 | return buddy->arena.main; 1160 | } 1161 | 1162 | static unsigned int buddy_relative_mode(struct buddy *buddy) { 1163 | return (unsigned int)buddy->buddy_flags & BUDDY_RELATIVE_MODE; 1164 | } 1165 | 1166 | static void buddy_toggle_virtual_slots(struct buddy *buddy, unsigned int state) { 1167 | size_t delta, memory_size, effective_memory_size; 1168 | struct buddy_tree *tree; 1169 | struct buddy_tree_pos pos; 1170 | 1171 | memory_size = buddy->memory_size; 1172 | /* Mask/unmask the virtual space if memory is not a power of two */ 1173 | effective_memory_size = buddy_effective_memory_size(buddy); 1174 | if (effective_memory_size == memory_size) { 1175 | return; 1176 | } 1177 | 1178 | /* Get the area that we need to mask and pad it to alignment */ 1179 | /* Node memory size is already aligned to buddy->alignment */ 1180 | delta = effective_memory_size - memory_size; 1181 | 1182 | tree = buddy_tree(buddy); 1183 | pos = buddy_tree_right_child(buddy_tree_root()); 1184 | while (delta) { 1185 | size_t current_pos_size = size_for_depth(buddy, buddy_tree_depth(pos)); 1186 | if (delta == current_pos_size) { 1187 | /* toggle current pos */ 1188 | if (state) { 1189 | buddy_tree_mark(tree, pos); 1190 | } 1191 | else { 1192 | buddy_tree_release(tree, pos); 1193 | } 1194 | break; 1195 | } 1196 | if (delta <= (current_pos_size / 2)) { 1197 | /* re-run for right child */ 1198 | pos = buddy_tree_right_child(pos); 1199 | continue; 1200 | } else { 1201 | /* toggle right child */ 1202 | if (state) { 1203 | buddy_tree_mark(tree, buddy_tree_right_child(pos)); 1204 | } 1205 | else { 1206 | buddy_tree_release(tree, buddy_tree_right_child(pos)); 1207 | } 1208 | /* reduce delta */ 1209 | delta -= current_pos_size / 2; 1210 | /* re-run for left child */ 1211 | pos = buddy_tree_left_child(pos); 1212 | continue; 1213 | } 1214 | } 1215 | } 1216 | 1217 | static void buddy_toggle_range_reservation(struct buddy *buddy, void *ptr, size_t requested_size, unsigned int state) { 1218 | unsigned char *dst, *main; 1219 | struct buddy_tree *tree; 1220 | size_t offset; 1221 | struct buddy_tree_pos pos; 1222 | 1223 | if (buddy == NULL) { 1224 | return; 1225 | } 1226 | if (ptr == NULL) { 1227 | return; 1228 | } 1229 | if (requested_size == 0) { 1230 | return; 1231 | } 1232 | dst = (unsigned char *)ptr; 1233 | main = buddy_main(buddy); 1234 | if ((dst < main) || ((dst + requested_size) > (main + buddy->memory_size))) { 1235 | return; 1236 | } 1237 | 1238 | /* Find the deepest position tracking this address */ 1239 | tree = buddy_tree(buddy); 1240 | offset = (size_t) (dst - main); 1241 | pos = deepest_position_for_offset(buddy, offset); 1242 | 1243 | /* Advance one position at a time and process */ 1244 | while (requested_size) { 1245 | if (state) { 1246 | buddy_tree_mark(tree, pos); 1247 | } 1248 | else { 1249 | buddy_tree_release(tree, pos); 1250 | } 1251 | requested_size = (requested_size < buddy->alignment) ? 0 : (requested_size - buddy->alignment); 1252 | pos.index++; 1253 | } 1254 | 1255 | return; 1256 | } 1257 | 1258 | /* Internal function that checks if there are any allocations 1259 | after the indicated relative memory index. Used to check if 1260 | the arena can be downsized. 1261 | The from argument is already adjusted for alignment by caller */ 1262 | static bool buddy_is_free(struct buddy *buddy, size_t from) { 1263 | struct buddy_tree *tree; 1264 | struct buddy_tree_interval query_range; 1265 | struct buddy_tree_pos pos; 1266 | size_t effective_memory_size, virtual_slots, to; 1267 | 1268 | effective_memory_size = buddy_effective_memory_size(buddy); 1269 | virtual_slots = buddy_virtual_slots(buddy); 1270 | to = effective_memory_size - 1271 | ((virtual_slots ? (virtual_slots + 1) : 1) * buddy->alignment); 1272 | 1273 | tree = buddy_tree(buddy); 1274 | 1275 | query_range.from = deepest_position_for_offset(buddy, from); 1276 | query_range.to = deepest_position_for_offset(buddy, to); 1277 | 1278 | pos = deepest_position_for_offset(buddy, from); 1279 | while(buddy_tree_valid(tree, pos) && (pos.index < query_range.to.index)) { 1280 | struct buddy_tree_interval current_test_range = buddy_tree_interval(tree, pos); 1281 | struct buddy_tree_interval parent_test_range = 1282 | buddy_tree_interval(tree, buddy_tree_parent(pos)); 1283 | while(buddy_tree_interval_contains(query_range, parent_test_range)) { 1284 | pos = buddy_tree_parent(pos); 1285 | current_test_range = parent_test_range; 1286 | parent_test_range = buddy_tree_interval(tree, buddy_tree_parent(pos)); 1287 | } 1288 | /* pos is now tracking an overlapping segment */ 1289 | if (! buddy_tree_is_free(tree, pos)) { 1290 | return false; 1291 | } 1292 | /* Advance check */ 1293 | pos = buddy_tree_right_adjacent(current_test_range.to); 1294 | } 1295 | return true; 1296 | } 1297 | 1298 | static struct buddy_embed_check buddy_embed_offset(size_t memory_size, size_t alignment) { 1299 | size_t buddy_size, offset; 1300 | struct buddy_embed_check check_result; 1301 | 1302 | memset(&check_result, 0, sizeof(check_result)); 1303 | check_result.can_fit = 1; 1304 | buddy_size = buddy_sizeof_alignment(memory_size, alignment); 1305 | if (buddy_size >= memory_size) { 1306 | check_result.can_fit = 0; 1307 | } 1308 | 1309 | offset = memory_size - buddy_size; 1310 | if (offset % BUDDY_ALIGNOF(struct buddy) != 0) { 1311 | buddy_size += offset % BUDDY_ALIGNOF(struct buddy); 1312 | if (buddy_size >= memory_size) { 1313 | check_result.can_fit = 0; 1314 | } 1315 | offset = memory_size - buddy_size; 1316 | } 1317 | 1318 | if (check_result.can_fit) { 1319 | check_result.offset = offset; 1320 | check_result.buddy_size = buddy_size; 1321 | } 1322 | return check_result; 1323 | } 1324 | 1325 | void buddy_debug(struct buddy *buddy) { 1326 | BUDDY_PRINTF("buddy allocator at: %p arena at: %p\n", (void *)buddy, (void *)buddy_main(buddy)); 1327 | BUDDY_PRINTF("memory size: %zu\n", buddy->memory_size); 1328 | BUDDY_PRINTF("mode: "); 1329 | if (buddy_relative_mode(buddy)) { 1330 | BUDDY_PRINTF("embedded"); 1331 | } else { 1332 | BUDDY_PRINTF("standard"); 1333 | } 1334 | BUDDY_PRINTF("\n"); 1335 | BUDDY_PRINTF("virtual slots: %zu\n", buddy_virtual_slots(buddy)); 1336 | BUDDY_PRINTF("allocator tree follows:\n"); 1337 | buddy_tree_debug(buddy_tree(buddy), buddy_tree_root(), buddy_effective_memory_size(buddy)); 1338 | } 1339 | 1340 | /* 1341 | * A buddy allocation tree 1342 | */ 1343 | 1344 | struct buddy_tree { 1345 | size_t upper_pos_bound; 1346 | size_t size_for_order_offset; 1347 | uint8_t order; 1348 | uint8_t flags; 1349 | /* 1350 | * struct padding rules mean that there are 1351 | * 16/48 bits available until the next increment 1352 | */ 1353 | }; 1354 | 1355 | enum buddy_tree_flags { 1356 | BUDDY_TREE_CHANGE_TRACKING = 1, 1357 | }; 1358 | 1359 | struct internal_position { 1360 | size_t local_offset; 1361 | size_t bitset_location; 1362 | }; 1363 | 1364 | static inline size_t size_for_order(uint8_t order, uint8_t to); 1365 | static inline size_t buddy_tree_index_internal(struct buddy_tree_pos pos); 1366 | static struct buddy_tree_pos buddy_tree_leftmost_child_internal(size_t tree_order); 1367 | static struct internal_position buddy_tree_internal_position_order( 1368 | size_t tree_order, struct buddy_tree_pos pos); 1369 | static struct internal_position buddy_tree_internal_position_tree( 1370 | struct buddy_tree *t, struct buddy_tree_pos pos); 1371 | static void buddy_tree_grow(struct buddy_tree *t, uint8_t desired_order); 1372 | static void buddy_tree_shrink(struct buddy_tree *t, uint8_t desired_order); 1373 | static void update_parent_chain(struct buddy_tree *t, struct buddy_tree_pos pos, 1374 | struct internal_position pos_internal, size_t size_current); 1375 | static inline unsigned char *buddy_tree_bits(struct buddy_tree *t); 1376 | static void buddy_tree_populate_size_for_order(struct buddy_tree *t); 1377 | static inline size_t buddy_tree_size_for_order(struct buddy_tree *t, uint8_t to); 1378 | static void write_to_internal_position(struct buddy_tree* t, struct internal_position pos, size_t value); 1379 | static size_t read_from_internal_position(unsigned char *bitset, struct internal_position pos); 1380 | static inline unsigned char compare_with_internal_position(unsigned char *bitset, struct internal_position pos, size_t value); 1381 | 1382 | #ifdef BUDDY_EXPERIMENTAL_CHANGE_TRACKING 1383 | static inline void buddy_tree_track_change(struct buddy_tree* t, unsigned char* addr, size_t length); 1384 | #endif /* BUDDY_EXPERIMENTAL_CHANGE_TRACKING */ 1385 | 1386 | static inline size_t size_for_order(uint8_t order, uint8_t to) { 1387 | size_t result = 0; 1388 | size_t multi = 1u; 1389 | while (order != to) { 1390 | result += order * multi; 1391 | order--; 1392 | multi *= 2; 1393 | } 1394 | return result; 1395 | } 1396 | 1397 | static inline struct internal_position buddy_tree_internal_position_order( 1398 | size_t tree_order, struct buddy_tree_pos pos) { 1399 | struct internal_position p; 1400 | size_t total_offset, local_index; 1401 | 1402 | p.local_offset = tree_order - buddy_tree_depth(pos) + 1; 1403 | total_offset = size_for_order((uint8_t) tree_order, (uint8_t) p.local_offset); 1404 | local_index = buddy_tree_index_internal(pos); 1405 | p.bitset_location = total_offset + (p.local_offset * local_index); 1406 | return p; 1407 | } 1408 | 1409 | static inline struct internal_position buddy_tree_internal_position_tree( 1410 | struct buddy_tree *t, struct buddy_tree_pos pos) { 1411 | struct internal_position p; 1412 | size_t total_offset, local_index; 1413 | 1414 | p.local_offset = t->order - buddy_tree_depth(pos) + 1; 1415 | total_offset = buddy_tree_size_for_order(t, (uint8_t) p.local_offset); 1416 | local_index = buddy_tree_index_internal(pos); 1417 | p.bitset_location = total_offset + (p.local_offset * local_index); 1418 | return p; 1419 | } 1420 | 1421 | static size_t buddy_tree_sizeof(uint8_t order) { 1422 | size_t tree_size, bitset_size, size_for_order_size; 1423 | 1424 | tree_size = sizeof(struct buddy_tree); 1425 | /* Account for the bitset */ 1426 | bitset_size = bitset_sizeof(size_for_order(order, 0)); 1427 | if (bitset_size % sizeof(size_t)) { 1428 | bitset_size += (bitset_size % sizeof(size_t)); 1429 | } 1430 | /* Account for the size_for_order memoization */ 1431 | size_for_order_size = ((order+2) * sizeof(size_t)); 1432 | return tree_size + bitset_size + size_for_order_size; 1433 | } 1434 | 1435 | static struct buddy_tree *buddy_tree_init(unsigned char *at, uint8_t order) { 1436 | size_t size = buddy_tree_sizeof(order); 1437 | struct buddy_tree *t = (struct buddy_tree*) at; 1438 | memset(at, 0, size); 1439 | t->order = order; 1440 | t->upper_pos_bound = two_to_the_power_of(t->order); 1441 | buddy_tree_populate_size_for_order(t); 1442 | return t; 1443 | } 1444 | 1445 | #ifdef BUDDY_EXPERIMENTAL_CHANGE_TRACKING 1446 | static void buddy_tree_enable_change_tracking(struct buddy_tree* t) { 1447 | t->flags |= BUDDY_TREE_CHANGE_TRACKING; 1448 | } 1449 | #endif /* BUDDY_EXPERIMENTAL_CHANGE_TRACKING */ 1450 | 1451 | static void buddy_tree_resize(struct buddy_tree *t, uint8_t desired_order) { 1452 | if (t->order == desired_order) { 1453 | return; 1454 | } 1455 | if (t->order < desired_order) { 1456 | buddy_tree_grow(t, desired_order); 1457 | } else { 1458 | buddy_tree_shrink(t, desired_order); 1459 | } 1460 | } 1461 | 1462 | static void buddy_tree_grow(struct buddy_tree *t, uint8_t desired_order) { 1463 | struct buddy_tree_pos pos; 1464 | 1465 | while (desired_order > t->order) { 1466 | /* Grow the tree a single order at a time */ 1467 | size_t current_order = t->order; 1468 | struct buddy_tree_pos current_pos = buddy_tree_leftmost_child_internal(current_order); 1469 | struct buddy_tree_pos next_pos = buddy_tree_leftmost_child_internal(current_order + 1u); 1470 | while(current_order) { 1471 | /* Get handles into the rows at the tracked depth */ 1472 | struct internal_position current_internal = buddy_tree_internal_position_order( 1473 | t->order, current_pos); 1474 | struct internal_position next_internal = buddy_tree_internal_position_order( 1475 | t->order + 1u, next_pos); 1476 | 1477 | /* There are this many nodes at the current level */ 1478 | size_t node_count = two_to_the_power_of(current_order - 1u); 1479 | 1480 | /* Transfer the bits*/ 1481 | bitset_shift_right(buddy_tree_bits(t), 1482 | current_internal.bitset_location /* from here */, 1483 | current_internal.bitset_location + (current_internal.local_offset * node_count) /* up to here */, 1484 | next_internal.bitset_location - current_internal.bitset_location /* by */); 1485 | 1486 | /* Clear right section */ 1487 | bitset_clear_range(buddy_tree_bits(t), 1488 | bitset_range(next_internal.bitset_location + (next_internal.local_offset * node_count), 1489 | next_internal.bitset_location + (next_internal.local_offset * node_count * 2) - 1)); 1490 | 1491 | /* Handle the upper level */ 1492 | current_order -= 1u; 1493 | current_pos = buddy_tree_parent(current_pos); 1494 | next_pos = buddy_tree_parent(next_pos); 1495 | } 1496 | /* Advance the order and refresh the root */ 1497 | t->order += 1u; 1498 | t->upper_pos_bound = two_to_the_power_of(t->order); 1499 | buddy_tree_populate_size_for_order(t); 1500 | 1501 | /* Update the root */ 1502 | pos = buddy_tree_right_child(buddy_tree_root()); 1503 | update_parent_chain(t, pos, buddy_tree_internal_position_tree(t, pos), 0); 1504 | } 1505 | } 1506 | 1507 | static void buddy_tree_shrink(struct buddy_tree *t, uint8_t desired_order) { 1508 | size_t current_order, next_order, node_count; 1509 | struct buddy_tree_pos left_start; 1510 | struct internal_position current_internal, next_internal; 1511 | 1512 | while (desired_order < t->order) { 1513 | if (!buddy_tree_can_shrink(t)) { 1514 | return; 1515 | } 1516 | 1517 | /* Shrink the tree a single order at a time */ 1518 | current_order = t->order; 1519 | next_order = current_order - 1; 1520 | 1521 | left_start = buddy_tree_left_child(buddy_tree_root()); 1522 | while(buddy_tree_valid(t, left_start)) { 1523 | /* Get handles into the rows at the tracked depth */ 1524 | current_internal = buddy_tree_internal_position_order(current_order, left_start); 1525 | next_internal = buddy_tree_internal_position_order(next_order, buddy_tree_parent(left_start)); 1526 | 1527 | /* There are this many nodes at the current level */ 1528 | node_count = two_to_the_power_of(left_start.depth - 1u); 1529 | 1530 | /* Transfer the bits*/ 1531 | bitset_shift_left(buddy_tree_bits(t), 1532 | current_internal.bitset_location /* from here */, 1533 | current_internal.bitset_location + (current_internal.local_offset * node_count / 2) /* up to here */, 1534 | current_internal.bitset_location - next_internal.bitset_location/* at here */); 1535 | 1536 | /* Handle the lower level */ 1537 | left_start = buddy_tree_left_child(left_start); 1538 | } 1539 | 1540 | /* Advance the order */ 1541 | t->order = (uint8_t) next_order; 1542 | t->upper_pos_bound = two_to_the_power_of(t->order); 1543 | buddy_tree_populate_size_for_order(t); 1544 | } 1545 | } 1546 | 1547 | static bool buddy_tree_valid(struct buddy_tree *t, struct buddy_tree_pos pos) { 1548 | return pos.index && (pos.index < t->upper_pos_bound); 1549 | } 1550 | 1551 | static uint8_t buddy_tree_order(struct buddy_tree *t) { 1552 | return t->order; 1553 | } 1554 | 1555 | static struct buddy_tree_pos buddy_tree_root(void) { 1556 | struct buddy_tree_pos identity = { 1, 1 }; 1557 | return identity; 1558 | } 1559 | 1560 | static struct buddy_tree_pos buddy_tree_leftmost_child(struct buddy_tree *t) { 1561 | return buddy_tree_leftmost_child_internal(t->order); 1562 | } 1563 | 1564 | static struct buddy_tree_pos buddy_tree_leftmost_child_internal(size_t tree_order) { 1565 | struct buddy_tree_pos result; 1566 | result.index = two_to_the_power_of(tree_order - 1u); 1567 | result.depth = tree_order; 1568 | return result; 1569 | } 1570 | 1571 | static inline size_t buddy_tree_depth(struct buddy_tree_pos pos) { 1572 | return pos.depth; 1573 | } 1574 | 1575 | static inline struct buddy_tree_pos buddy_tree_left_child(struct buddy_tree_pos pos) { 1576 | pos.index *= 2; 1577 | pos.depth++; 1578 | return pos; 1579 | } 1580 | 1581 | static inline struct buddy_tree_pos buddy_tree_right_child(struct buddy_tree_pos pos) { 1582 | pos.index *= 2; 1583 | pos.index++; 1584 | pos.depth++; 1585 | return pos; 1586 | } 1587 | 1588 | static inline struct buddy_tree_pos buddy_tree_sibling(struct buddy_tree_pos pos) { 1589 | pos.index ^= 1; 1590 | return pos; 1591 | } 1592 | 1593 | static inline struct buddy_tree_pos buddy_tree_parent(struct buddy_tree_pos pos) { 1594 | pos.index /= 2; 1595 | pos.depth--; 1596 | return pos; 1597 | } 1598 | 1599 | static struct buddy_tree_pos buddy_tree_right_adjacent(struct buddy_tree_pos pos) { 1600 | if (((pos.index + 1) ^ pos.index) > pos.index) { 1601 | return INVALID_POS; 1602 | } 1603 | pos.index++; 1604 | return pos; 1605 | } 1606 | 1607 | static size_t buddy_tree_index(struct buddy_tree_pos pos) { 1608 | return buddy_tree_index_internal(pos); 1609 | } 1610 | 1611 | static inline size_t buddy_tree_index_internal(struct buddy_tree_pos pos) { 1612 | /* Clear out the highest bit, this gives us the index 1613 | * in a row of sibling nodes */ 1614 | size_t mask = two_to_the_power_of(pos.depth - 1u); 1615 | size_t result = pos.index & ~mask; 1616 | return result; 1617 | } 1618 | 1619 | static inline unsigned char *buddy_tree_bits(struct buddy_tree *t) { 1620 | return ((unsigned char *) t) + sizeof(*t); 1621 | } 1622 | 1623 | static void buddy_tree_populate_size_for_order(struct buddy_tree *t) { 1624 | size_t bitset_offset = bitset_sizeof(size_for_order(t->order, 0)); 1625 | if (bitset_offset % sizeof(size_t)) { 1626 | bitset_offset += (bitset_offset % sizeof(size_t)); 1627 | } 1628 | t->size_for_order_offset = bitset_offset / sizeof(size_t); 1629 | t->size_for_order_offset++; 1630 | for (size_t i = 0; i <= t->order; i++) { 1631 | *((size_t *)(((unsigned char *) t) + sizeof(*t)) + t->size_for_order_offset + i) = size_for_order(t->order, (uint8_t) i); 1632 | } 1633 | } 1634 | 1635 | static inline size_t buddy_tree_size_for_order(struct buddy_tree *t, 1636 | uint8_t to) { 1637 | return *((size_t *)(((unsigned char *) t) + sizeof(*t)) + t->size_for_order_offset + to); 1638 | } 1639 | 1640 | static void write_to_internal_position(struct buddy_tree* t, struct internal_position pos, size_t value) { 1641 | unsigned char *bitset = buddy_tree_bits(t); 1642 | struct bitset_range clear_range = bitset_range(pos.bitset_location, pos.bitset_location + pos.local_offset - 1); 1643 | 1644 | bitset_clear_range(bitset, clear_range); 1645 | if (value) { 1646 | bitset_set_range(bitset, bitset_range(pos.bitset_location, pos.bitset_location+value-1)); 1647 | } 1648 | 1649 | #ifdef BUDDY_EXPERIMENTAL_CHANGE_TRACKING 1650 | /* Ignore the same bucket condition - we don't care if we track one more byte here */ 1651 | buddy_tree_track_change(t, bitset, clear_range.to_bucket - clear_range.from_bucket + 1); 1652 | #endif 1653 | } 1654 | 1655 | static size_t read_from_internal_position(unsigned char *bitset, struct internal_position pos) { 1656 | if (! bitset_test(bitset, pos.bitset_location)) { 1657 | return 0; /* Fast test without complete extraction */ 1658 | } 1659 | return bitset_count_range(bitset, bitset_range(pos.bitset_location, pos.bitset_location+pos.local_offset-1)); 1660 | } 1661 | 1662 | static inline unsigned char compare_with_internal_position(unsigned char *bitset, struct internal_position pos, size_t value) { 1663 | return bitset_test(bitset, pos.bitset_location+value-1); 1664 | } 1665 | 1666 | static struct buddy_tree_interval buddy_tree_interval(struct buddy_tree *t, struct buddy_tree_pos pos) { 1667 | struct buddy_tree_interval result; 1668 | size_t depth; 1669 | 1670 | result.from = pos; 1671 | result.to = pos; 1672 | depth = pos.depth; 1673 | while (depth != t->order) { 1674 | result.from = buddy_tree_left_child(result.from); 1675 | result.to = buddy_tree_right_child(result.to); 1676 | depth += 1; 1677 | } 1678 | return result; 1679 | } 1680 | 1681 | static bool buddy_tree_interval_contains(struct buddy_tree_interval outer, 1682 | struct buddy_tree_interval inner) { 1683 | return (inner.from.index >= outer.from.index) 1684 | && (inner.from.index <= outer.to.index) 1685 | && (inner.to.index >= outer.from.index) 1686 | && (inner.to.index <= outer.to.index); 1687 | } 1688 | 1689 | static struct buddy_tree_walk_state buddy_tree_walk_state_root(void) { 1690 | struct buddy_tree_walk_state state; 1691 | memset(&state, 0, sizeof(state)); 1692 | state.starting_pos = buddy_tree_root(); 1693 | state.current_pos = buddy_tree_root(); 1694 | return state; 1695 | } 1696 | 1697 | static unsigned int buddy_tree_walk(struct buddy_tree *t, struct buddy_tree_walk_state *state) { 1698 | do { 1699 | if (state->going_up) { 1700 | if (state->current_pos.index == state->starting_pos.index) { 1701 | state->walk_done = 1; 1702 | state->going_up = 0; 1703 | } else if (state->current_pos.index & 1u) { 1704 | state->current_pos = buddy_tree_parent(state->current_pos); /* Ascend */ 1705 | } else { 1706 | state->current_pos = buddy_tree_right_adjacent(state->current_pos); /* Descend right */ 1707 | state->going_up = 0; 1708 | } 1709 | } else if (buddy_tree_valid(t, buddy_tree_left_child(state->current_pos))) { 1710 | /* Descend left */ 1711 | state->current_pos = buddy_tree_left_child(state->current_pos); 1712 | } else { /* Ascend */ 1713 | state->going_up = 1; 1714 | } 1715 | } while(state->going_up); 1716 | return ! state->walk_done; 1717 | } 1718 | 1719 | static size_t buddy_tree_status(struct buddy_tree *t, struct buddy_tree_pos pos) { 1720 | struct internal_position internal = buddy_tree_internal_position_tree(t, pos); 1721 | return read_from_internal_position(buddy_tree_bits(t), internal); 1722 | } 1723 | 1724 | static void buddy_tree_mark(struct buddy_tree *t, struct buddy_tree_pos pos) { 1725 | /* Calling mark on a used position is a bug in caller */ 1726 | struct internal_position internal = buddy_tree_internal_position_tree(t, pos); 1727 | 1728 | /* Mark the node as used */ 1729 | write_to_internal_position(t, internal, internal.local_offset); 1730 | 1731 | /* Update the tree upwards */ 1732 | update_parent_chain(t, pos, internal, internal.local_offset); 1733 | } 1734 | 1735 | static enum buddy_tree_release_status buddy_tree_release(struct buddy_tree *t, struct buddy_tree_pos pos) { 1736 | /* Calling release on an unused or a partially-used position a bug in caller */ 1737 | struct internal_position internal = buddy_tree_internal_position_tree(t, pos); 1738 | 1739 | if (read_from_internal_position(buddy_tree_bits(t), internal) != internal.local_offset) { 1740 | return BUDDY_TREE_RELEASE_FAIL_PARTIALLY_USED; 1741 | } 1742 | 1743 | /* Mark the node as unused */ 1744 | write_to_internal_position(t, internal, 0); 1745 | 1746 | /* Update the tree upwards */ 1747 | update_parent_chain(t, pos, internal, 0); 1748 | 1749 | return BUDDY_TREE_RELEASE_SUCCESS; 1750 | } 1751 | 1752 | static void update_parent_chain(struct buddy_tree *t, struct buddy_tree_pos pos, 1753 | struct internal_position pos_internal, size_t size_current) { 1754 | size_t size_sibling, size_parent, target_parent; 1755 | unsigned char *bits = buddy_tree_bits(t); 1756 | 1757 | while (pos.index != 1) { 1758 | pos_internal.bitset_location += pos_internal.local_offset 1759 | - (2 * pos_internal.local_offset * (pos.index & 1u)); 1760 | size_sibling = read_from_internal_position(bits, pos_internal); 1761 | 1762 | pos = buddy_tree_parent(pos); 1763 | pos_internal = buddy_tree_internal_position_tree(t, pos); 1764 | size_parent = read_from_internal_position(bits, pos_internal); 1765 | 1766 | target_parent = (size_current || size_sibling) 1767 | * ((size_current <= size_sibling ? size_current : size_sibling) + 1); 1768 | if (target_parent == size_parent) { 1769 | return; 1770 | } 1771 | 1772 | write_to_internal_position(t, pos_internal, target_parent); 1773 | size_current = target_parent; 1774 | }; 1775 | } 1776 | 1777 | static struct buddy_tree_pos buddy_tree_find_free(struct buddy_tree *t, uint8_t target_depth) { 1778 | struct buddy_tree_pos current_pos, left_pos, right_pos; 1779 | uint8_t target_status; 1780 | size_t current_depth, right_status; 1781 | struct internal_position left_internal, right_internal; 1782 | unsigned char *tree_bits; 1783 | 1784 | current_pos = buddy_tree_root(); 1785 | target_status = target_depth - 1; 1786 | current_depth = buddy_tree_depth(current_pos); 1787 | if (buddy_tree_status(t, current_pos) > target_status) { 1788 | return INVALID_POS; /* No position available down the tree */ 1789 | } 1790 | tree_bits = buddy_tree_bits(t); 1791 | while (current_depth != target_depth) { 1792 | /* Advance criteria */ 1793 | target_status -= 1; 1794 | current_depth += 1; 1795 | 1796 | left_pos = buddy_tree_left_child(current_pos); 1797 | right_pos = buddy_tree_sibling(left_pos); 1798 | 1799 | left_internal = buddy_tree_internal_position_tree(t, left_pos); 1800 | 1801 | right_internal = left_internal; 1802 | right_internal.bitset_location += right_internal.local_offset; /* advance to the right */ 1803 | 1804 | if (compare_with_internal_position(tree_bits, left_internal, target_status+1)) { /* left branch is busy, pick right */ 1805 | current_pos = right_pos; 1806 | } else if (compare_with_internal_position(tree_bits, right_internal, target_status+1)) { /* right branch is busy, pick left */ 1807 | current_pos = left_pos; 1808 | } else { 1809 | /* One of the child nodes must be read in order to compare it to its sibling. */ 1810 | right_status = read_from_internal_position(tree_bits, right_internal); 1811 | if (right_status) { 1812 | if (compare_with_internal_position(tree_bits, left_internal, right_status)) { 1813 | current_pos = left_pos; /* Left is equal or more busy than right, prefer left */ 1814 | } else { 1815 | current_pos = right_pos; 1816 | } 1817 | } else { /* Right is empty, prefer left */ 1818 | current_pos = left_pos; 1819 | } 1820 | } 1821 | } 1822 | return current_pos; 1823 | } 1824 | 1825 | static bool buddy_tree_is_free(struct buddy_tree *t, struct buddy_tree_pos pos) { 1826 | if (buddy_tree_status(t, pos)) { 1827 | return false; 1828 | } 1829 | pos = buddy_tree_parent(pos); 1830 | while(buddy_tree_valid(t, pos)) { 1831 | struct internal_position internal = buddy_tree_internal_position_tree(t, pos); 1832 | size_t value = read_from_internal_position(buddy_tree_bits(t), internal); 1833 | if (value) { 1834 | return value != internal.local_offset; 1835 | } 1836 | pos = buddy_tree_parent(pos); 1837 | } 1838 | return true; 1839 | } 1840 | 1841 | static bool buddy_tree_can_shrink(struct buddy_tree *t) { 1842 | struct internal_position root_internal; 1843 | size_t root_value; 1844 | 1845 | if (buddy_tree_status(t, buddy_tree_right_child(buddy_tree_root())) != 0) { 1846 | return false; /* Refusing to shrink with right subtree still used! */ 1847 | } 1848 | root_internal = buddy_tree_internal_position_tree(t, buddy_tree_root()); 1849 | root_value = read_from_internal_position(buddy_tree_bits(t), root_internal); 1850 | if (root_value == root_internal.local_offset) { 1851 | return false; /* Refusing to shrink with the root fully-allocated! */ 1852 | } 1853 | return true; 1854 | } 1855 | 1856 | #ifdef BUDDY_EXPERIMENTAL_CHANGE_TRACKING 1857 | static struct buddy* buddy_tree_buddy(struct buddy_tree* t) { 1858 | return (struct buddy*)(((unsigned char*)t) - sizeof(struct buddy)); 1859 | } 1860 | #endif /* BUDDY_EXPERIMENTAL_CHANGE_TRACKING */ 1861 | 1862 | static void buddy_tree_debug(struct buddy_tree *t, struct buddy_tree_pos pos, 1863 | size_t start_size) { 1864 | struct buddy_tree_walk_state state = buddy_tree_walk_state_root(); 1865 | state.current_pos = pos; 1866 | do { 1867 | struct internal_position pos_internal = buddy_tree_internal_position_tree(t, state.current_pos); 1868 | size_t pos_status = read_from_internal_position(buddy_tree_bits(t), pos_internal); 1869 | size_t pos_size = start_size >> ((buddy_tree_depth(state.current_pos) - 1u) % ((sizeof(size_t) * CHAR_BIT)-1)); 1870 | BUDDY_PRINTF("%.*s", 1871 | (int) buddy_tree_depth(state.current_pos), 1872 | " "); 1873 | BUDDY_PRINTF("pos index: %zu pos depth: %zu status: %zu bitset-len: %zu bitset-at: %zu", 1874 | state.current_pos.index, state.current_pos.depth, pos_status, 1875 | pos_internal.local_offset, pos_internal.bitset_location); 1876 | if (pos_status == pos_internal.local_offset) { 1877 | BUDDY_PRINTF(" size: %zu", pos_size); 1878 | } 1879 | BUDDY_PRINTF("\n"); 1880 | } while (buddy_tree_walk(t, &state)); 1881 | } 1882 | 1883 | unsigned int buddy_tree_check_invariant(struct buddy_tree *t, struct buddy_tree_pos pos) { 1884 | unsigned int fail = 0; 1885 | struct buddy_tree_walk_state state = buddy_tree_walk_state_root(); 1886 | state.current_pos = pos; 1887 | do { 1888 | struct internal_position current_internal = buddy_tree_internal_position_tree(t, pos); 1889 | size_t current_status = read_from_internal_position(buddy_tree_bits(t), current_internal); 1890 | size_t left_child_status = buddy_tree_status(t, buddy_tree_left_child(pos)); 1891 | size_t right_child_status = buddy_tree_status(t, buddy_tree_right_child(pos)); 1892 | unsigned int violated = 0; 1893 | 1894 | if (left_child_status || right_child_status) { 1895 | size_t min = left_child_status <= right_child_status 1896 | ? left_child_status : right_child_status; 1897 | if (current_status != (min + 1)) { 1898 | violated = 1; 1899 | } 1900 | } else { 1901 | if ((current_status > 0) && (current_status < current_internal.local_offset)) { 1902 | violated = 1; 1903 | } 1904 | } 1905 | 1906 | if (violated) { 1907 | fail = 1; 1908 | BUDDY_PRINTF("invariant violation at position [ index: %zu depth: %zu ]!\n", pos.index, pos.depth); 1909 | BUDDY_PRINTF("current: %zu left %zu right %zu max %zu\n", 1910 | current_status, left_child_status, right_child_status, current_internal.local_offset); 1911 | } 1912 | 1913 | } while (buddy_tree_walk(t, &state)); 1914 | return fail; 1915 | } 1916 | 1917 | /* 1918 | * Calculate tree fragmentation based on free slots. 1919 | * Based on https://asawicki.info/news_1757_a_metric_for_memory_fragmentation 1920 | */ 1921 | static unsigned char buddy_tree_fragmentation(struct buddy_tree *t) { 1922 | const unsigned char fractional_bits = 8; 1923 | const unsigned char fractional_mask = 255; 1924 | 1925 | uint8_t tree_order; 1926 | size_t root_status, quality, total_free_size, virtual_size, quality_percent; 1927 | struct buddy_tree_walk_state state; 1928 | 1929 | tree_order = buddy_tree_order(t); 1930 | root_status = buddy_tree_status(t, buddy_tree_root()); 1931 | if (root_status == 0) { /* Emptry tree */ 1932 | return 0; 1933 | } 1934 | 1935 | quality = 0; 1936 | total_free_size = 0; 1937 | 1938 | state = buddy_tree_walk_state_root(); 1939 | do { 1940 | size_t pos_status = buddy_tree_status(t, state.current_pos); 1941 | if (pos_status == 0) { 1942 | /* Empty node, process */ 1943 | virtual_size = two_to_the_power_of((tree_order - state.current_pos.depth) % ((sizeof(size_t) * CHAR_BIT)-1)); 1944 | quality += (virtual_size * virtual_size); 1945 | total_free_size += virtual_size; 1946 | /* Ascend */ 1947 | state.going_up = 1; 1948 | } else if (pos_status == (tree_order - state.current_pos.depth + 1)) { 1949 | /* Busy node, ascend */ 1950 | state.going_up = 1; 1951 | } 1952 | } while (buddy_tree_walk(t, &state)); 1953 | 1954 | if (total_free_size == 0) { /* Fully-allocated tree */ 1955 | return 0; 1956 | } 1957 | 1958 | quality_percent = (integer_square_root(quality) << fractional_bits) / total_free_size; 1959 | quality_percent *= quality_percent; 1960 | quality_percent >>= fractional_bits; 1961 | return fractional_mask - (quality_percent & fractional_mask); 1962 | } 1963 | 1964 | #ifdef BUDDY_EXPERIMENTAL_CHANGE_TRACKING 1965 | static inline void buddy_tree_track_change(struct buddy_tree* t, unsigned char* addr, size_t length) { 1966 | struct buddy_change_tracker *header; 1967 | 1968 | if (!(t->flags && BUDDY_TREE_CHANGE_TRACKING)) { 1969 | return; 1970 | } 1971 | 1972 | header = (struct buddy_change_tracker *) buddy_main(buddy_tree_buddy(t)); 1973 | header->tracker(header->context, addr, length); 1974 | } 1975 | #endif /* BUDDY_EXPERIMENTAL_CHANGE_TRACKING */ 1976 | 1977 | /* 1978 | * A char-backed bitset implementation 1979 | */ 1980 | 1981 | size_t bitset_sizeof(size_t elements) { 1982 | return ((elements) + CHAR_BIT - 1u) / CHAR_BIT; 1983 | } 1984 | 1985 | static uint8_t bitset_index_mask[8] = {1, 2, 4, 8, 16, 32, 64, 128}; 1986 | 1987 | static inline void bitset_set(unsigned char *bitset, size_t pos) { 1988 | size_t bucket = pos / CHAR_BIT; 1989 | size_t index = pos % CHAR_BIT; 1990 | bitset[bucket] |= bitset_index_mask[index]; 1991 | } 1992 | 1993 | static inline void bitset_clear(unsigned char *bitset, size_t pos) { 1994 | size_t bucket = pos / CHAR_BIT; 1995 | size_t index = pos % CHAR_BIT; 1996 | bitset[bucket] &= ~bitset_index_mask[index]; 1997 | } 1998 | 1999 | static inline bool bitset_test(const unsigned char *bitset, size_t pos) { 2000 | size_t bucket = pos / CHAR_BIT; 2001 | size_t index = pos % CHAR_BIT; 2002 | return bitset[bucket] & bitset_index_mask[index]; 2003 | } 2004 | 2005 | static const uint8_t bitset_char_mask[8][8] = { 2006 | {1, 3, 7, 15, 31, 63, 127, 255}, 2007 | {0, 2, 6, 14, 30, 62, 126, 254}, 2008 | {0, 0, 4, 12, 28, 60, 124, 252}, 2009 | {0, 0, 0, 8, 24, 56, 120, 248}, 2010 | {0, 0, 0, 0, 16, 48, 112, 240}, 2011 | {0, 0, 0, 0, 0, 32, 96, 224}, 2012 | {0, 0, 0, 0, 0, 0, 64, 192}, 2013 | {0, 0, 0, 0, 0, 0, 0, 128}, 2014 | }; 2015 | 2016 | static inline struct bitset_range bitset_range(size_t from_pos, size_t to_pos) { 2017 | struct bitset_range range = {0}; 2018 | range.from_bucket = from_pos / CHAR_BIT; 2019 | range.to_bucket = to_pos / CHAR_BIT; 2020 | 2021 | range.from_index = from_pos % CHAR_BIT; 2022 | range.to_index = to_pos % CHAR_BIT; 2023 | return range; 2024 | } 2025 | 2026 | static void bitset_set_range(unsigned char *bitset, struct bitset_range range) { 2027 | if (range.from_bucket == range.to_bucket) { 2028 | bitset[range.from_bucket] |= bitset_char_mask[range.from_index][range.to_index]; 2029 | } else { 2030 | bitset[range.from_bucket] |= bitset_char_mask[range.from_index][7]; 2031 | bitset[range.to_bucket] |= bitset_char_mask[0][range.to_index]; 2032 | while(++range.from_bucket != range.to_bucket) { 2033 | bitset[range.from_bucket] = 255u; 2034 | } 2035 | } 2036 | } 2037 | 2038 | static void bitset_clear_range(unsigned char* bitset, struct bitset_range range) { 2039 | if (range.from_bucket == range.to_bucket) { 2040 | bitset[range.from_bucket] &= ~bitset_char_mask[range.from_index][range.to_index]; 2041 | } 2042 | else { 2043 | bitset[range.from_bucket] &= ~bitset_char_mask[range.from_index][7]; 2044 | bitset[range.to_bucket] &= ~bitset_char_mask[0][range.to_index]; 2045 | while (++range.from_bucket != range.to_bucket) { 2046 | bitset[range.from_bucket] = 0; 2047 | } 2048 | } 2049 | } 2050 | 2051 | static size_t bitset_count_range(unsigned char *bitset, struct bitset_range range) { 2052 | size_t result; 2053 | 2054 | if (range.from_bucket == range.to_bucket) { 2055 | return popcount_byte(bitset[range.from_bucket] & bitset_char_mask[range.from_index][range.to_index]); 2056 | } 2057 | 2058 | result = popcount_byte(bitset[range.from_bucket] & bitset_char_mask[range.from_index][7]) 2059 | + popcount_byte(bitset[range.to_bucket] & bitset_char_mask[0][range.to_index]); 2060 | while(++range.from_bucket != range.to_bucket) { 2061 | result += popcount_byte(bitset[range.from_bucket]); 2062 | } 2063 | return result; 2064 | } 2065 | 2066 | static void bitset_shift_left(unsigned char *bitset, size_t from_pos, size_t to_pos, size_t by) { 2067 | size_t length = to_pos - from_pos; 2068 | for(size_t i = 0; i < length; i++) { 2069 | size_t at = from_pos + i; 2070 | if (bitset_test(bitset, at)) { 2071 | bitset_set(bitset, at-by); 2072 | } else { 2073 | bitset_clear(bitset, at-by); 2074 | } 2075 | bitset_clear(bitset, at); 2076 | } 2077 | } 2078 | 2079 | static void bitset_shift_right(unsigned char *bitset, size_t from_pos, size_t to_pos, size_t by) { 2080 | ssize_t length = (ssize_t) to_pos - (ssize_t) from_pos; 2081 | while (length >= 0) { 2082 | size_t at = from_pos + (size_t) length; 2083 | if (bitset_test(bitset, at)) { 2084 | bitset_set(bitset, at+by); 2085 | } else { 2086 | bitset_clear(bitset, at+by); 2087 | } 2088 | bitset_clear(bitset, at); 2089 | length -= 1; 2090 | } 2091 | } 2092 | 2093 | void bitset_debug(unsigned char *bitset, size_t length) { 2094 | for (size_t i = 0; i < length; i++) { 2095 | BUDDY_PRINTF("%zu: %d\n", i, bitset_test(bitset, i) > 0); 2096 | } 2097 | } 2098 | 2099 | /* 2100 | Bits 2101 | */ 2102 | 2103 | static const unsigned char popcount_lookup[256] = { 2104 | 0,1,1,2,1,2,2,3,1,2,2,3,2,3,3,4,1,2,2,3,2,3,3,4,2,3,3,4,3,4,4,5, 2105 | 1,2,2,3,2,3,3,4,2,3,3,4,3,4,4,5,2,3,3,4,3,4,4,5,3,4,4,5,4,5,5,6, 2106 | 1,2,2,3,2,3,3,4,2,3,3,4,3,4,4,5,2,3,3,4,3,4,4,5,3,4,4,5,4,5,5,6, 2107 | 2,3,3,4,3,4,4,5,3,4,4,5,4,5,5,6,3,4,4,5,4,5,5,6,4,5,5,6,5,6,6,7, 2108 | 1,2,2,3,2,3,3,4,2,3,3,4,3,4,4,5,2,3,3,4,3,4,4,5,3,4,4,5,4,5,5,6, 2109 | 2,3,3,4,3,4,4,5,3,4,4,5,4,5,5,6,3,4,4,5,4,5,5,6,4,5,5,6,5,6,6,7, 2110 | 2,3,3,4,3,4,4,5,3,4,4,5,4,5,5,6,3,4,4,5,4,5,5,6,4,5,5,6,5,6,6,7, 2111 | 3,4,4,5,4,5,5,6,4,5,5,6,5,6,6,7,4,5,5,6,5,6,6,7,5,6,6,7,6,7,7,8 2112 | }; 2113 | 2114 | static inline unsigned int popcount_byte(unsigned char b) { 2115 | return popcount_lookup[b]; 2116 | } 2117 | 2118 | /* Returns the highest set bit position for the given value. Returns zero for zero. */ 2119 | static size_t highest_bit_position(size_t value) { 2120 | size_t result = 0; 2121 | /* some other millennia when size_t becomes 128-bit this will break :) */ 2122 | #if SIZE_MAX == 0xFFFFFFFFFFFFFFFF 2123 | const size_t all_set[] = {4294967295, 65535, 255, 15, 7, 3, 1}; 2124 | const size_t count[] = {32, 16, 8, 4, 2, 1, 1}; 2125 | #elif SIZE_MAX == 0xFFFFFFFF 2126 | const size_t all_set[] = {65535, 255, 15, 7, 3, 1}; 2127 | const size_t count[] = {16, 8, 4, 2, 1, 1}; 2128 | #else 2129 | #error Unsupported platform 2130 | #endif 2131 | 2132 | for (size_t i = 0; i < (sizeof all_set / sizeof *all_set); i++) { 2133 | if (value >= all_set[i]) { 2134 | value >>= count[i]; 2135 | result += count[i]; 2136 | } 2137 | } 2138 | return result + value; 2139 | } 2140 | 2141 | static inline size_t ceiling_power_of_two(size_t value) { 2142 | value += !value; /* branchless x -> { 1 for 0, x for x } */ 2143 | return two_to_the_power_of(highest_bit_position(value + value - 1)-1); 2144 | } 2145 | 2146 | static inline size_t two_to_the_power_of(size_t order) { 2147 | return ((size_t)1) << order; 2148 | } 2149 | 2150 | static inline size_t integer_square_root(size_t op) { 2151 | /* by Martin Guy, 1985 - http://medialab.freaknet.org/martin/src/sqrt/ */ 2152 | size_t result = 0; 2153 | size_t cursor = (SIZE_MAX - (SIZE_MAX >> 1)) >> 1; /* second-to-top bit set */ 2154 | while (cursor > op) { 2155 | cursor >>= 2; 2156 | } 2157 | /* "cursor" starts at the highest power of four <= than the argument. */ 2158 | while (cursor != 0) { 2159 | if (op >= result + cursor) { 2160 | op -= result + cursor; 2161 | result += 2 * cursor; 2162 | } 2163 | result >>= 1; 2164 | cursor >>= 2; 2165 | } 2166 | return result; 2167 | } 2168 | 2169 | #ifdef __cplusplus 2170 | #ifndef BUDDY_CPP_MANGLED 2171 | } 2172 | #endif 2173 | #endif 2174 | 2175 | #endif /* BUDDY_ALLOC_IMPLEMENTATION */ 2176 | -------------------------------------------------------------------------------- /compile_flags.txt: -------------------------------------------------------------------------------- 1 | -DBUDDY_ALLOC_IMPLEMENTATION 2 | -DBUDDY_EXPERIMENTAL_CHANGE_TRACKING 3 | -------------------------------------------------------------------------------- /test-fuzz.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | 4 | #ifndef BUDDY_ALLOC_IMPLEMENTATION 5 | #define BUDDY_ALLOC_IMPLEMENTATION 6 | #endif 7 | #include "buddy_alloc.h" 8 | 9 | int main(void) 10 | { 11 | unsigned char buffer[16384]; 12 | void *allocs[255]; 13 | unsigned char *meta = malloc(buddy_sizeof_alignment(16384, 64)); 14 | struct buddy *b = buddy_init_alignment(meta, buffer, 16384, 64); 15 | struct buddy_tree *tree = buddy_tree(b); 16 | for (;;) { 17 | int slot = getchar(); 18 | if (slot == EOF) { 19 | break; 20 | } 21 | if (allocs[slot]) { 22 | if (getchar() % 2) { 23 | buddy_free(b, allocs[slot]); 24 | } else { 25 | int size = slot*slot; 26 | if (getchar() % 2) { 27 | size = size/2; 28 | } else { 29 | size = size*2; 30 | } 31 | buddy_realloc(b, allocs[slot], size, true); 32 | } 33 | assert(buddy_tree_check_invariant(tree, buddy_tree_root()) == 0); 34 | allocs[slot] = 0; 35 | } else { 36 | int size = getchar(); 37 | if (size == EOF) { 38 | break; 39 | } 40 | size *= size; // Exercise large slots too 41 | allocs[slot] = buddy_malloc(b, size); 42 | assert(buddy_tree_check_invariant(tree, buddy_tree_root()) == 0); 43 | } 44 | } 45 | } 46 | -------------------------------------------------------------------------------- /testcxx.cpp: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2020-2021 Stanislav Paskalev 3 | */ 4 | 5 | // Just include buddy_alloc from C++ 6 | #ifndef BUDDY_ALLOC_IMPLEMENTATION 7 | #define BUDDY_ALLOC_IMPLEMENTATION 8 | #endif 9 | #include "buddy_alloc.h" 10 | #undef BUDDY_ALLOC_IMPLEMENTATION 11 | 12 | int main(int argc, char *argv[]) 13 | { 14 | (void)argc; 15 | (void)argv; 16 | return 0; 17 | } -------------------------------------------------------------------------------- /tests.c: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2020-2021 Stanislav Paskalev 3 | */ 4 | 5 | 6 | #define START_TEST printf("Running test: %s in %s:%d\n", __func__, __FILE__, __LINE__); 7 | #define _POSIX_C_SOURCE 200112L 8 | 9 | #include 10 | #include 11 | #include 12 | #include 13 | 14 | #ifndef BUDDY_EXPERIMENTAL_CHANGE_TRACKING 15 | #define BUDDY_EXPERIMENTAL_CHANGE_TRACKING 16 | #endif 17 | 18 | #ifndef BUDDY_ALLOC_IMPLEMENTATION 19 | #define BUDDY_ALLOC_IMPLEMENTATION 20 | #endif 21 | #include "buddy_alloc.h" 22 | #undef BUDDY_ALLOC_IMPLEMENTATION 23 | 24 | /* 25 | * The tests are written with 64-bit arch in mind. 26 | * To allow running them on 32-bit the following macros are used 27 | * 28 | * PSS(x) - Platform-specific size. Does nothing on 64 bit. Halves value on 32 bit. 29 | * OF(x,y) - One of - picks the first argument on 64 bit and the second on 32 bit. 30 | */ 31 | 32 | #if SIZE_MAX == 0xFFFFFFFFFFFFFFFF 33 | #define PSS(x) (x) 34 | #define OF(x, y) (x) 35 | #elif SIZE_MAX == 0xFFFFFFFF 36 | #define PSS(x) ((x)/2) 37 | #define OF(x, y) (y) 38 | #else 39 | #error Unsupported platform 40 | #endif 41 | 42 | /* 43 | * This is a mini-hack for Windows, that allows us to quick-fail on assertions 44 | */ 45 | #ifdef _MSC_VER 46 | #undef assert 47 | #define assert(x) if(!(x)){ printf("Assertion failed on ( " #x " )"); exit(1); } 48 | void *my_aligned_malloc(size_t size, size_t alignment) { 49 | return _aligned_malloc(size, alignment); 50 | } 51 | void my_aligned_free(void *aligned_ptr) { 52 | _aligned_free(aligned_ptr); 53 | } 54 | #else 55 | void *my_aligned_malloc(size_t size, size_t alignment) { 56 | void *ptr = NULL; 57 | assert(posix_memalign(&ptr, alignment, size) == 0); 58 | return ptr; 59 | } 60 | void my_aligned_free(void *aligned_ptr) { 61 | free(aligned_ptr); 62 | } 63 | #endif 64 | 65 | void test_highest_bit_position(void) { 66 | assert(highest_bit_position(1) == 1); 67 | assert(highest_bit_position(2) == 2); 68 | assert(highest_bit_position(3) == 2); 69 | assert(highest_bit_position(4) == 3); 70 | assert(highest_bit_position(SIZE_MAX-1) == (sizeof(size_t) * CHAR_BIT)); 71 | assert(highest_bit_position(SIZE_MAX) == (sizeof(size_t) * CHAR_BIT)); 72 | } 73 | 74 | void test_ceiling_power_of_two(void) { 75 | assert(ceiling_power_of_two(0) == 1); 76 | assert(ceiling_power_of_two(1) == 1); 77 | assert(ceiling_power_of_two(2) == 2); 78 | assert(ceiling_power_of_two(3) == 4); 79 | assert(ceiling_power_of_two(4) == 4); 80 | assert(ceiling_power_of_two(5) == 8); 81 | assert(ceiling_power_of_two(6) == 8); 82 | assert(ceiling_power_of_two(7) == 8); 83 | assert(ceiling_power_of_two(8) == 8); 84 | } 85 | 86 | void test_popcount_byte(void) { 87 | for (size_t i = 0; i < 256; i++) { 88 | unsigned char c = (unsigned char) i; 89 | assert(popcount_byte(c) == (popcount_byte(c / 2) + (c & 1))); 90 | } 91 | } 92 | 93 | void test_bitset_basic(void) { 94 | unsigned char buf[4] = {0}; 95 | START_TEST; 96 | assert(bitset_sizeof(7) == 1); 97 | assert(bitset_sizeof(8) == 1); 98 | assert(bitset_sizeof(9) == 2); 99 | assert(!bitset_test(buf, 0)); 100 | bitset_set(buf, 0); 101 | assert(bitset_test(buf, 0)); 102 | } 103 | 104 | void test_bitset_range(void) { 105 | unsigned char buf[4] = {0}; 106 | size_t bitset_length = 32; 107 | START_TEST; 108 | for (size_t i = 0; i < bitset_length; i++) { 109 | for (size_t j = 0; j <= i; j++) { 110 | memset(buf, 0, 4); 111 | bitset_set_range(buf, bitset_range(j, i)); 112 | for (size_t k = 0; k < bitset_length; k++) { 113 | if ((k >= j) && (k <= i)) { 114 | assert(bitset_test(buf, k)); 115 | } else { 116 | assert(!bitset_test(buf, k)); 117 | } 118 | } 119 | bitset_clear_range(buf, bitset_range(j, i)); 120 | for (size_t k = j; k < i; k++) { 121 | assert(!bitset_test(buf, k)); 122 | } 123 | } 124 | } 125 | } 126 | 127 | void test_bitset_shift(void) { 128 | unsigned char *buf = malloc(bitset_sizeof(16)); 129 | START_TEST; 130 | for (size_t i = 0; i < 16; i++) { 131 | bitset_clear(buf, i); 132 | } 133 | bitset_set(buf, 0); 134 | bitset_set(buf, 3); 135 | bitset_set(buf, 4); 136 | bitset_set(buf, 7); 137 | bitset_shift_right(buf, 0, 8, 4); 138 | assert(!bitset_test(buf, 0)); 139 | assert(!bitset_test(buf, 1)); 140 | assert(!bitset_test(buf, 2)); 141 | assert(!bitset_test(buf, 3)); 142 | assert(bitset_test(buf, 4)); 143 | assert(!bitset_test(buf, 5)); 144 | assert(!bitset_test(buf, 6)); 145 | assert(bitset_test(buf, 7)); 146 | assert(bitset_test(buf, 8)); 147 | assert(!bitset_test(buf, 9)); 148 | assert(!bitset_test(buf, 10)); 149 | assert(bitset_test(buf, 11)); 150 | assert(!bitset_test(buf, 12)); 151 | assert(!bitset_test(buf, 13)); 152 | assert(!bitset_test(buf, 14)); 153 | assert(!bitset_test(buf, 15)); 154 | bitset_shift_left(buf, 4, 12, 4); 155 | assert(bitset_test(buf, 0)); 156 | assert(!bitset_test(buf, 1)); 157 | assert(!bitset_test(buf, 2)); 158 | assert(bitset_test(buf, 3)); 159 | assert(bitset_test(buf, 4)); 160 | assert(!bitset_test(buf, 5)); 161 | assert(!bitset_test(buf, 6)); 162 | assert(bitset_test(buf, 7)); 163 | assert(!bitset_test(buf, 8)); 164 | assert(!bitset_test(buf, 9)); 165 | assert(!bitset_test(buf, 10)); 166 | assert(!bitset_test(buf, 11)); 167 | assert(!bitset_test(buf, 12)); 168 | assert(!bitset_test(buf, 13)); 169 | assert(!bitset_test(buf, 14)); 170 | assert(!bitset_test(buf, 15)); 171 | 172 | for (size_t i = 0; i < 16; i++) { 173 | bitset_set(buf, i); 174 | } 175 | bitset_shift_left(buf, 4, 5, 1); 176 | for (size_t i = 0; i < 16; i++) { 177 | if (i == 4) { 178 | assert(!bitset_test(buf, i)); 179 | } else { 180 | assert(bitset_test(buf, i)); 181 | } 182 | } 183 | free(buf); 184 | } 185 | 186 | void test_bitset_shift_invalid(void) { 187 | unsigned char buf[4096] = {0}; 188 | START_TEST; 189 | bitset_set_range(buf, bitset_range(1, 0)); /* no-op */ 190 | assert(!bitset_test(buf, 0)); 191 | assert(!bitset_test(buf, 1)); 192 | bitset_set_range(buf, bitset_range(0, 1)); 193 | assert(bitset_test(buf, 0)); 194 | assert(bitset_test(buf, 1)); 195 | bitset_clear_range(buf, bitset_range(1, 0)) /* no-op */; 196 | assert(bitset_test(buf, 0)); 197 | assert(bitset_test(buf, 1)); 198 | bitset_clear_range(buf, bitset_range(0, 1)); 199 | assert(!bitset_test(buf, 0)); 200 | assert(!bitset_test(buf, 1)); 201 | } 202 | 203 | void test_bitset_debug(void) { 204 | unsigned char buf[4096] = {0}; 205 | START_TEST; 206 | bitset_set(buf, 0); 207 | bitset_clear(buf, 1); 208 | bitset_debug(buf, 2); /* code coverage */ 209 | } 210 | 211 | void test_buddy_init_null(void) { 212 | unsigned char data_buf[4096]; 213 | unsigned char *buddy_buf = malloc(buddy_sizeof(4096)); 214 | START_TEST; 215 | { 216 | struct buddy *buddy = buddy_init(NULL, data_buf, 4096); 217 | assert(buddy == NULL); 218 | } 219 | { 220 | struct buddy *buddy = buddy_init(buddy_buf, NULL, 4096); 221 | assert(buddy == NULL); 222 | } 223 | free(buddy_buf); 224 | } 225 | 226 | void test_buddy_init_overlap(void) { 227 | unsigned char data_buf[4096]; 228 | struct buddy *buddy; 229 | START_TEST; 230 | buddy = buddy_init(data_buf, data_buf, 4096); 231 | assert(buddy == NULL); 232 | } 233 | 234 | struct wrong_alignment { 235 | size_t placement_1; 236 | unsigned char buffer_1[4096]; 237 | size_t placement_2; 238 | unsigned char buffer_2[4096]; 239 | }; 240 | 241 | void test_buddy_misalignment(void) { 242 | struct buddy *buddy; 243 | struct wrong_alignment wa = {0}; 244 | START_TEST; 245 | buddy = buddy_init(wa.buffer_1 + 1, wa.buffer_2, 4096); 246 | assert(buddy == NULL); 247 | buddy = buddy_init(wa.buffer_1, wa.buffer_2+1, 2048); 248 | assert(buddy == NULL); 249 | 250 | } 251 | 252 | void test_buddy_embed_misalignment(void) { 253 | struct wrong_alignment wa = {0}; 254 | struct buddy *buddy; 255 | START_TEST; 256 | buddy = buddy_embed(wa.buffer_1 + 1, 2048); 257 | assert(buddy == NULL); 258 | } 259 | 260 | void test_buddy_invalid_datasize(void) { 261 | unsigned char data_buf[4096]; 262 | unsigned char *buddy_buf = malloc(buddy_sizeof(4096)); 263 | struct buddy *buddy; 264 | START_TEST; 265 | assert(buddy_sizeof(0) == 0); 266 | assert(buddy_sizeof(BUDDY_ALLOC_ALIGN-1) == 0); 267 | buddy = buddy_init(buddy_buf, data_buf, 0); 268 | assert(buddy == NULL); 269 | free(buddy_buf); 270 | } 271 | 272 | void test_buddy_init(void) { 273 | unsigned char data_buf[4096]; 274 | unsigned char *buddy_buf = malloc(buddy_sizeof(4096)); 275 | struct buddy *buddy; 276 | START_TEST; 277 | buddy = buddy_init(buddy_buf, data_buf, 4096); 278 | assert(buddy != NULL); 279 | free(buddy_buf); 280 | } 281 | 282 | void test_buddy_init_virtual_slots(void) { 283 | unsigned char data_buf[1024]; 284 | unsigned char *buddy_buf = malloc(buddy_sizeof(1024)); 285 | struct buddy *buddy; 286 | START_TEST; 287 | buddy = buddy_init(buddy_buf, data_buf, 1020); 288 | assert(buddy != NULL); 289 | free(buddy_buf); 290 | } 291 | 292 | void test_buddy_init_non_power_of_two_memory_01(void) { 293 | size_t buddy_size = PSS(4096); 294 | unsigned char *buddy_buf = malloc(buddy_sizeof(buddy_size)); 295 | unsigned char *data_buf = malloc(buddy_size); 296 | size_t cutoff = PSS(256); 297 | struct buddy *buddy; 298 | START_TEST; 299 | buddy = buddy_init(buddy_buf, data_buf, buddy_size-cutoff); 300 | assert(buddy != NULL); 301 | for (size_t i = 0; i < 60; i++) { 302 | assert(buddy_malloc(buddy, BUDDY_ALLOC_ALIGN) != NULL); 303 | } 304 | assert(buddy_malloc(buddy, BUDDY_ALLOC_ALIGN) == NULL); 305 | free(buddy_buf); 306 | free(data_buf); 307 | } 308 | 309 | void test_buddy_init_non_power_of_two_memory_02(void) { 310 | size_t buddy_size = PSS(4096); 311 | size_t cutoff = PSS(256+(sizeof(size_t)/2)); 312 | unsigned char *buddy_buf = malloc(buddy_sizeof(buddy_size)); 313 | unsigned char *data_buf = malloc(buddy_size); 314 | struct buddy *buddy; 315 | START_TEST; 316 | buddy = buddy_init(buddy_buf, data_buf, buddy_size-cutoff); 317 | assert(buddy != NULL); 318 | for (size_t i = 0; i < 59; i++) { 319 | assert(buddy_malloc(buddy, BUDDY_ALLOC_ALIGN) != NULL); 320 | } 321 | assert(buddy_malloc(buddy, BUDDY_ALLOC_ALIGN) == NULL); 322 | free(buddy_buf); 323 | free(data_buf); 324 | } 325 | 326 | void test_buddy_init_non_power_of_two_memory_03(void) { 327 | size_t buddy_size = PSS(4096); 328 | unsigned char *buddy_buf = malloc(buddy_sizeof(buddy_size)); 329 | unsigned char *data_buf = malloc(buddy_size); 330 | size_t cutoff = PSS(256-(sizeof(size_t)/2)); 331 | struct buddy *buddy; 332 | START_TEST; 333 | buddy = buddy_init(buddy_buf, data_buf, buddy_size-cutoff); 334 | assert(buddy != NULL); 335 | for (size_t i = 0; i < 60; i++) { 336 | assert(buddy_malloc(buddy, BUDDY_ALLOC_ALIGN) != NULL); 337 | } 338 | assert(buddy_malloc(buddy, BUDDY_ALLOC_ALIGN) == NULL); 339 | free(buddy_buf); 340 | free(data_buf); 341 | } 342 | 343 | void test_buddy_resize_noop(void) { 344 | unsigned char *buddy_buf = malloc(buddy_sizeof(1024)); 345 | unsigned char data_buf[1024]; 346 | struct buddy *buddy; 347 | START_TEST; 348 | buddy = buddy_init(buddy_buf, data_buf, 1024); 349 | assert(buddy != NULL); 350 | assert(buddy_resize(buddy, 1024) == buddy); 351 | free(buddy_buf); 352 | } 353 | 354 | void test_buddy_resize_up_within_reserved(void) { 355 | unsigned char *buddy_buf = malloc(buddy_sizeof(1024)); 356 | unsigned char data_buf[1024]; 357 | struct buddy *buddy; 358 | START_TEST; 359 | buddy = buddy_init(buddy_buf, data_buf, 768); 360 | assert(buddy != NULL); 361 | assert(buddy_resize(buddy, 896) == buddy); 362 | assert(buddy_resize(buddy, 768) == buddy); 363 | free(buddy_buf); 364 | } 365 | 366 | void test_buddy_resize_up_at_reserved(void) { 367 | unsigned char *buddy_buf = malloc(buddy_sizeof(1024)); 368 | unsigned char data_buf[1024]; 369 | struct buddy *buddy; 370 | START_TEST; 371 | buddy = buddy_init(buddy_buf, data_buf, 768); 372 | assert(buddy != NULL); 373 | assert(buddy_resize(buddy, 1024) == buddy); 374 | assert(buddy_resize(buddy, 768) == buddy); 375 | free(buddy_buf); 376 | } 377 | 378 | void test_buddy_resize_up_after_reserved(void) { 379 | unsigned char *buddy_buf = malloc(buddy_sizeof(4096)); 380 | unsigned char data_buf[4096]; 381 | struct buddy *buddy; 382 | START_TEST; 383 | buddy = buddy_init(buddy_buf, data_buf, 768); 384 | assert(buddy != NULL); 385 | assert(buddy_resize(buddy, 2048) == buddy); 386 | assert(buddy_resize(buddy, 768) == buddy); 387 | free(buddy_buf); 388 | } 389 | 390 | void test_buddy_resize_down_to_virtual(void) { 391 | unsigned char *buddy_buf = malloc(buddy_sizeof(1024)); 392 | unsigned char data_buf[1024]; 393 | struct buddy *buddy; 394 | START_TEST; 395 | buddy = buddy_init(buddy_buf, data_buf, 1024); 396 | assert(buddy != NULL); 397 | assert(buddy_resize(buddy, 832) == buddy); 398 | assert(buddy_resize(buddy, 1024) == buddy); 399 | free(buddy_buf); 400 | } 401 | 402 | void test_buddy_resize_down_to_virtual_partial(void) { 403 | unsigned char *buddy_buf = malloc(buddy_sizeof(1024)); 404 | unsigned char data_buf[1024]; 405 | struct buddy *buddy; 406 | START_TEST; 407 | buddy = buddy_init(buddy_buf, data_buf, 1024); 408 | assert(buddy != NULL); 409 | assert(buddy_resize(buddy, 832-1) == buddy); 410 | assert(buddy_resize(buddy, 1024) == buddy); 411 | free(buddy_buf); 412 | } 413 | 414 | void test_buddy_resize_down_within_reserved(void) { 415 | unsigned char *buddy_buf = malloc(buddy_sizeof(1024)); 416 | unsigned char data_buf[1024]; 417 | struct buddy *buddy; 418 | START_TEST; 419 | buddy = buddy_init(buddy_buf, data_buf, 768); 420 | assert(buddy != NULL); 421 | assert(buddy_resize(buddy, 640) == buddy); 422 | assert(buddy_resize(buddy, 768) == buddy); 423 | free(buddy_buf); 424 | } 425 | 426 | void test_buddy_resize_down_within_reserved_failure(void) { 427 | unsigned char *buddy_buf = malloc(buddy_sizeof(1024)); 428 | unsigned char data_buf[1024]; 429 | struct buddy *buddy; 430 | void *r512, *r256; 431 | START_TEST; 432 | buddy = buddy_init(buddy_buf, data_buf, 768); 433 | assert(buddy != NULL); 434 | r512 = buddy_malloc(buddy, 512); 435 | assert(r512 != NULL); 436 | r256 = buddy_malloc(buddy, 256); 437 | assert(r256 != NULL); 438 | buddy_free(buddy, r512); 439 | assert(buddy_resize(buddy, 640) == NULL); 440 | free(buddy_buf); 441 | } 442 | 443 | void test_buddy_resize_down_at_reserved(void) { 444 | unsigned char *buddy_buf = malloc(buddy_sizeof(1024)); 445 | unsigned char data_buf[1024]; 446 | struct buddy *buddy; 447 | START_TEST; 448 | buddy = buddy_init(buddy_buf, data_buf, 768); 449 | assert(buddy != NULL); 450 | assert(buddy_resize(buddy, 512) == buddy); 451 | assert(buddy_resize(buddy, 768) == buddy); 452 | free(buddy_buf); 453 | } 454 | 455 | void test_buddy_resize_down_before_reserved(void) { 456 | unsigned char *buddy_buf = malloc(buddy_sizeof(1024)); 457 | unsigned char data_buf[1024]; 458 | struct buddy *buddy; 459 | START_TEST; 460 | buddy = buddy_init(buddy_buf, data_buf, 768); 461 | assert(buddy != NULL); 462 | assert(buddy_resize(buddy, 448) == buddy); 463 | assert(buddy_resize(buddy, 768) == buddy); 464 | free(buddy_buf); 465 | } 466 | 467 | void test_buddy_resize_down_already_used(void) { 468 | unsigned char *buddy_buf = malloc(buddy_sizeof(4096)); 469 | unsigned char data_buf[4096]; 470 | struct buddy *buddy; 471 | void *r1024; 472 | START_TEST; 473 | buddy = buddy_init(buddy_buf, data_buf, 4096); 474 | assert(buddy != NULL); 475 | r1024 = buddy_malloc(buddy, 1024); 476 | assert(r1024 == data_buf); 477 | assert(buddy_resize(buddy, 256) == NULL); 478 | free(buddy_buf); 479 | } 480 | 481 | void test_buddy_resize_multiple(void) { 482 | size_t buddy_size = 1 << 20; 483 | size_t max_buddy_size = 1 << 29; 484 | unsigned char *data_buf = malloc(max_buddy_size); 485 | unsigned char *buddy_buf = malloc(buddy_sizeof(max_buddy_size)); 486 | struct buddy *buddy; 487 | START_TEST; 488 | buddy = buddy_init(buddy_buf, data_buf, buddy_size); 489 | for (size_t shift = 1; shift <= 9; shift++) { 490 | while (buddy_malloc(buddy, PSS(64))); /* Fill it in */ 491 | buddy = buddy_resize(buddy, buddy_size << shift); 492 | assert(buddy_can_shrink(buddy)); 493 | } 494 | free(data_buf); 495 | free(buddy_buf); 496 | } 497 | 498 | void test_buddy_resize_embedded_up_within_reserved(void) { 499 | unsigned char data_buf[4096]; 500 | struct buddy *buddy; 501 | START_TEST; 502 | buddy = buddy_embed(data_buf, 768 + buddy_sizeof(768)); 503 | assert(buddy != NULL); 504 | buddy = buddy_resize(buddy, 896 + buddy_sizeof(896)); 505 | assert(buddy != NULL); 506 | assert(buddy_malloc(buddy, 512) == data_buf); 507 | assert(buddy_malloc(buddy, 256) == data_buf+512); 508 | assert(buddy_malloc(buddy, 128) == data_buf+512+256); 509 | /* No more space */ 510 | assert(buddy_malloc(buddy, 64) == NULL); 511 | assert(buddy_malloc(buddy, 32) == NULL); 512 | assert(buddy_malloc(buddy, 16) == NULL); 513 | assert(buddy_malloc(buddy, 8) == NULL); 514 | } 515 | 516 | void test_buddy_resize_embedded_up_at_reserved(void) { 517 | unsigned char data_buf[4096]; 518 | struct buddy *buddy; 519 | START_TEST; 520 | buddy = buddy_embed(data_buf, 768 + buddy_sizeof(768)); 521 | assert(buddy != NULL); 522 | assert(buddy_malloc(buddy, 1024) == NULL); 523 | buddy = buddy_resize(buddy, 1024 + (sizeof(size_t)*OF(4,5)) + buddy_sizeof(1024)); 524 | assert(buddy != NULL); 525 | assert(buddy_malloc(buddy, 1024) == data_buf); 526 | assert(buddy_malloc(buddy, 1024) == NULL); 527 | } 528 | 529 | void test_buddy_resize_embedded_up_after_reserved(void) { 530 | unsigned char data_buf[4096]; 531 | struct buddy *buddy; 532 | START_TEST; 533 | buddy = buddy_embed(data_buf, 768 + (sizeof(size_t)*2) + buddy_sizeof(768)); 534 | assert(buddy != NULL); 535 | assert(buddy_malloc(buddy, 1024) == NULL); 536 | buddy = buddy_resize(buddy, 2048 + (sizeof(size_t)*OF(3,9)) + buddy_sizeof(2048)); 537 | assert(buddy != NULL); 538 | assert(buddy_malloc(buddy, 1024) == data_buf); 539 | assert(buddy_malloc(buddy, 1024) == data_buf + 1024); 540 | assert(buddy_malloc(buddy, 1024) == NULL); 541 | } 542 | 543 | void test_buddy_resize_embedded_down_within_reserved(void) { 544 | unsigned char data_buf[4096]; 545 | struct buddy *buddy; 546 | START_TEST; 547 | buddy = buddy_embed(data_buf, 768 + buddy_sizeof(768)); 548 | assert(buddy != NULL); 549 | buddy = buddy_resize(buddy, 640 + buddy_sizeof(640)); 550 | assert(buddy != NULL); 551 | assert(buddy_malloc(buddy, 512) == data_buf); 552 | assert(buddy_malloc(buddy, 64) == data_buf+512); 553 | assert(buddy_malloc(buddy, 64) == data_buf+512+64); 554 | assert(buddy_malloc(buddy, 64) == NULL); 555 | } 556 | 557 | void test_buddy_resize_embedded_down_within_reserved_failure(void) { 558 | unsigned char data_buf[4096]; 559 | struct buddy *buddy; 560 | void *r512, *r256; 561 | START_TEST; 562 | buddy = buddy_embed(data_buf, 768 + buddy_sizeof(768)); 563 | assert(buddy != NULL); 564 | r512 = buddy_malloc(buddy, 512); 565 | assert(r512 != NULL); 566 | r256 = buddy_malloc(buddy, 256); 567 | assert(r256 != NULL); 568 | buddy_free(buddy, r512); 569 | assert(buddy_resize(buddy, 640 + buddy_sizeof(640)) == NULL); 570 | } 571 | 572 | void test_buddy_resize_embedded_down_at_reserved(void) { 573 | unsigned char data_buf[4096]; 574 | struct buddy *buddy; 575 | START_TEST; 576 | buddy = buddy_embed(data_buf, 768 + buddy_sizeof(768)); 577 | assert(buddy != NULL); 578 | assert(buddy_resize(buddy, 512 + buddy_sizeof(512)) != NULL); 579 | } 580 | 581 | void test_buddy_resize_embedded_down_before_reserved(void) { 582 | unsigned char data_buf[4096]; 583 | struct buddy *buddy; 584 | START_TEST; 585 | buddy = buddy_embed(data_buf, 768 + buddy_sizeof(768)); 586 | assert(buddy != NULL); 587 | assert(buddy_resize(buddy, 448 + buddy_sizeof(448)) != NULL); 588 | } 589 | 590 | void test_buddy_resize_embedded_down_already_used(void) { 591 | unsigned char data_buf[4096]; 592 | struct buddy *buddy; 593 | START_TEST; 594 | buddy = buddy_embed(data_buf, 4096); 595 | assert(buddy != NULL); 596 | buddy_malloc(buddy, 1024); 597 | assert(buddy_resize(buddy, 256 + buddy_sizeof(256)) == NULL); 598 | } 599 | 600 | void test_buddy_resize_embedded_too_small(void) { 601 | unsigned char data_buf[4096]; 602 | struct buddy *buddy; 603 | START_TEST; 604 | buddy = buddy_embed(data_buf, 4096); 605 | assert(buddy != NULL); 606 | assert(buddy_resize(buddy, 1) == NULL); 607 | } 608 | 609 | void test_buddy_resize_embedded_multiple(void) { 610 | size_t buddy_size = 1 << 20; 611 | size_t max_buddy_size = 1 << 29; 612 | unsigned char *data_buf = malloc(max_buddy_size); 613 | struct buddy *buddy; 614 | START_TEST; 615 | buddy = buddy_embed(data_buf, buddy_size); 616 | for (size_t shift = 1; shift <= 9; shift++) { 617 | while (buddy_malloc(buddy, PSS(64))); /* Fill it in */ 618 | buddy = buddy_resize(buddy, buddy_size << shift); 619 | assert(buddy_can_shrink(buddy)); 620 | } 621 | free(data_buf); 622 | } 623 | 624 | void test_buddy_debug(void) { 625 | unsigned char *buddy_buf = malloc(buddy_sizeof(4096)); 626 | unsigned char data_buf[4096]; 627 | struct buddy *buddy; 628 | START_TEST; 629 | buddy = buddy_init_alignment(buddy_buf, data_buf, 64, 32); 630 | buddy_debug(buddy); /* code coverage */ 631 | buddy = buddy_init_alignment(buddy_buf, data_buf, 4096, 4096); 632 | buddy_debug(buddy); /* code coverage */ 633 | buddy = buddy_embed(data_buf, 4096); 634 | buddy_debug(buddy); /* code coverage */ 635 | free(buddy_buf); 636 | } 637 | 638 | void test_buddy_can_shrink(void) { 639 | unsigned char data_buf[4096]; 640 | unsigned char buddy_buf[4096]; 641 | struct buddy *buddy = NULL; 642 | void *r2048_1, *r2048_2, *r4096; 643 | START_TEST; 644 | assert(buddy_can_shrink(buddy) == 0); 645 | buddy = buddy_init(buddy_buf, data_buf, 4096); 646 | assert(buddy_can_shrink(buddy) == 1); 647 | r2048_1 = buddy_malloc(buddy, 2048); 648 | assert(r2048_1 == data_buf); 649 | r2048_2 = buddy_malloc(buddy, 2048); 650 | assert(r2048_2 == data_buf+2048); 651 | buddy_free(buddy, r2048_1); 652 | assert(buddy_can_shrink(buddy) == 0); 653 | buddy_free(buddy, r2048_2); 654 | r4096 = buddy_malloc(buddy, 4096); 655 | assert(r4096 == data_buf); 656 | assert(buddy_can_shrink(buddy) == 0); 657 | } 658 | 659 | void test_buddy_arena_size(void) { 660 | unsigned char data_buf[4096]; 661 | unsigned char buddy_buf[4096]; 662 | struct buddy *buddy = NULL; 663 | START_TEST; 664 | assert(buddy_arena_size(buddy) == 0); 665 | buddy = buddy_init(buddy_buf, data_buf, 4096); 666 | assert(buddy_arena_size(buddy) == 4096); 667 | } 668 | 669 | void test_buddy_arena_free_size_01(void) { 670 | unsigned char *buddy_buf = malloc(buddy_sizeof(4096)); 671 | unsigned char data_buf[4096]; 672 | struct buddy *buddy; 673 | void *slot; 674 | START_TEST; 675 | buddy = buddy_init(buddy_buf, data_buf, 4096); 676 | assert(buddy_arena_free_size(buddy) == 4096); 677 | slot = buddy_malloc(buddy, 4096); 678 | assert(buddy_arena_free_size(buddy) == 0); 679 | buddy_free(buddy, slot); 680 | assert(buddy_arena_free_size(buddy) == 4096); 681 | free(buddy_buf); 682 | } 683 | 684 | void test_buddy_arena_free_size_02(void) { 685 | unsigned char *buddy_buf = malloc(buddy_sizeof(4096)); 686 | unsigned char data_buf[4096]; 687 | struct buddy *buddy; 688 | void *slot; 689 | START_TEST; 690 | buddy = buddy_init(buddy_buf, data_buf, 3072); 691 | assert(buddy_arena_free_size(buddy) == 3072); 692 | slot = buddy_malloc(buddy, 2048); 693 | assert(buddy_arena_free_size(buddy) == 1024); 694 | buddy_free(buddy, slot); 695 | assert(buddy_arena_free_size(buddy) == 3072); 696 | free(buddy_buf); 697 | } 698 | 699 | void test_buddy_arena_free_size_03(void) { 700 | unsigned char shared_buf[8192]; 701 | struct buddy *buddy; 702 | size_t current_free; 703 | void *slot; 704 | START_TEST; 705 | buddy = buddy_embed(shared_buf, 8192); 706 | current_free = buddy_arena_free_size(buddy); 707 | slot = buddy_malloc(buddy, 2048); 708 | assert(buddy_arena_free_size(buddy) == current_free - 2048); 709 | buddy_free(buddy, slot); 710 | assert(buddy_arena_free_size(buddy) == current_free); 711 | } 712 | 713 | void test_buddy_malloc_null(void) { 714 | START_TEST; 715 | assert(buddy_malloc(NULL, 1024) == NULL); 716 | } 717 | 718 | void test_buddy_malloc_zero(void) { 719 | unsigned char *buddy_buf = malloc(buddy_sizeof(4096)); 720 | unsigned char data_buf[4096]; 721 | struct buddy *buddy; 722 | START_TEST; 723 | buddy = buddy_init(buddy_buf, data_buf, 4096); 724 | assert(buddy != NULL); 725 | /* This is implementation-defined in the standard! */ 726 | assert(buddy_malloc(buddy, 0) != NULL); 727 | free(buddy_buf); 728 | } 729 | 730 | void test_buddy_malloc_larger(void) { 731 | unsigned char *buddy_buf = malloc(buddy_sizeof(4096)); 732 | unsigned char data_buf[4096]; 733 | struct buddy *buddy; 734 | START_TEST; 735 | buddy = buddy_init(buddy_buf, data_buf, 4096); 736 | assert(buddy != NULL); 737 | assert(buddy_malloc(buddy, 8192) == NULL); 738 | free(buddy_buf); 739 | } 740 | 741 | void test_buddy_malloc_basic_01(void) { 742 | unsigned char *buddy_buf = malloc(buddy_sizeof(1024)); 743 | unsigned char data_buf[1024]; 744 | struct buddy *buddy; 745 | START_TEST; 746 | buddy = buddy_init(buddy_buf, data_buf, 1024); 747 | assert(buddy != NULL); 748 | assert(buddy_malloc(buddy, 1024) == data_buf); 749 | assert(buddy_malloc(buddy, 1024) == NULL); 750 | buddy_free(buddy, data_buf); 751 | assert(buddy_malloc(buddy, 1024) == data_buf); 752 | assert(buddy_malloc(buddy, 1024) == NULL); 753 | free(buddy_buf); 754 | } 755 | 756 | void test_buddy_malloc_basic_02(void) { 757 | unsigned char *buddy_buf = malloc(buddy_sizeof(4096)); 758 | unsigned char data_buf[4096]; 759 | struct buddy *buddy; 760 | START_TEST; 761 | buddy = buddy_init(buddy_buf, data_buf, 4096); 762 | assert(buddy != NULL); 763 | assert(buddy_malloc(buddy, 2048) == data_buf); 764 | assert(buddy_malloc(buddy, 2048) == data_buf+2048); 765 | assert(buddy_malloc(buddy, 2048) == NULL); 766 | buddy_free(buddy, data_buf); 767 | buddy_free(buddy, data_buf+2048); 768 | assert(buddy_malloc(buddy, 2048) == data_buf); 769 | assert(buddy_malloc(buddy, 2048) == data_buf+2048); 770 | assert(buddy_malloc(buddy, 2048) == NULL); 771 | free(buddy_buf); 772 | } 773 | 774 | void test_buddy_malloc_basic_03(void) { 775 | unsigned char *buddy_buf = malloc(buddy_sizeof(4096)); 776 | unsigned char data_buf[4096]; 777 | struct buddy *buddy; 778 | START_TEST; 779 | buddy = buddy_init(buddy_buf, data_buf, 4096); 780 | assert(buddy != NULL); 781 | assert(buddy_malloc(buddy, 1024) == data_buf); 782 | assert(buddy_malloc(buddy, 2048) == data_buf+2048); 783 | assert(buddy_malloc(buddy, 1024) == data_buf+1024); 784 | assert(buddy_malloc(buddy, 1024) == NULL); 785 | buddy_free(buddy, data_buf+1024); 786 | buddy_free(buddy, data_buf+2048); 787 | buddy_free(buddy, data_buf); 788 | assert(buddy_malloc(buddy, 1024) == data_buf); 789 | assert(buddy_malloc(buddy, 2048) == data_buf+2048); 790 | assert(buddy_malloc(buddy, 1024) == data_buf+1024); 791 | assert(buddy_malloc(buddy, 1024) == NULL); 792 | free(buddy_buf); 793 | } 794 | 795 | void test_buddy_malloc_basic_04(void) { 796 | unsigned char *buddy_buf = malloc(buddy_sizeof(4096)); 797 | unsigned char data_buf[4096]; 798 | struct buddy *buddy; 799 | START_TEST; 800 | buddy = buddy_init(buddy_buf, data_buf, 4096); 801 | assert(buddy != NULL); 802 | assert(buddy_malloc(buddy, 64) == data_buf); 803 | assert(buddy_malloc(buddy, 32) == data_buf+64); 804 | free(buddy_buf); 805 | } 806 | 807 | void test_buddy_free_coverage(void) { 808 | unsigned char *buddy_buf = malloc(buddy_sizeof(4096)); 809 | unsigned char data_buf[4096]; 810 | struct buddy *buddy; 811 | START_TEST; 812 | buddy = buddy_init(buddy_buf, data_buf+1024, 1024); 813 | assert(buddy != NULL); 814 | buddy_free(NULL, NULL); 815 | buddy_free(NULL, data_buf+1024); 816 | buddy_free(buddy, NULL); 817 | buddy_free(buddy, data_buf); 818 | buddy_free(buddy, data_buf+2048); 819 | free(buddy_buf); 820 | } 821 | 822 | void test_buddy_free_alignment(void) { 823 | unsigned char *buddy_buf = malloc(buddy_sizeof(4096)); 824 | unsigned char data_buf[4096]; 825 | struct buddy *buddy; 826 | START_TEST; 827 | buddy = buddy_init(buddy_buf, data_buf, 4096); 828 | buddy_free(buddy, data_buf+1); 829 | free(buddy_buf); 830 | } 831 | 832 | void test_buddy_free_invalid_free_01(void) { 833 | size_t size = BUDDY_ALLOC_ALIGN * 2; 834 | unsigned char *buddy_buf = malloc(buddy_sizeof(size)); 835 | unsigned char *buddy_buf_control = malloc(buddy_sizeof(size)); 836 | unsigned char data_buf[4096]; 837 | struct buddy *buddy; 838 | void *r, *l; 839 | START_TEST; 840 | buddy = buddy_init(buddy_buf, data_buf, size); 841 | l = buddy_malloc(buddy, BUDDY_ALLOC_ALIGN); 842 | assert(l != NULL); 843 | r = buddy_malloc(buddy, BUDDY_ALLOC_ALIGN); 844 | assert(r != NULL); 845 | assert(l != r); 846 | buddy_free(buddy, r); 847 | memcpy(buddy_buf_control, buddy_buf, buddy_sizeof(size)); 848 | // double-free on a right node 849 | // that will propagate to a partially-allocated parent 850 | buddy_free(buddy, r); 851 | assert(memcmp(buddy_buf_control, buddy_buf, buddy_sizeof(size)) == 0); 852 | free(buddy_buf); 853 | free(buddy_buf_control); 854 | } 855 | 856 | void test_buddy_free_invalid_free_02(void) { 857 | size_t size = BUDDY_ALLOC_ALIGN * 2; 858 | unsigned char *buddy_buf = malloc(buddy_sizeof(size)); 859 | unsigned char *buddy_buf_control = malloc(buddy_sizeof(size)); 860 | unsigned char data_buf[4096]; 861 | struct buddy *buddy; 862 | void *r, *l; 863 | START_TEST; 864 | buddy = buddy_init(buddy_buf, data_buf, size); 865 | l = buddy_malloc(buddy, BUDDY_ALLOC_ALIGN); 866 | assert(l != NULL); 867 | r = buddy_malloc(buddy, BUDDY_ALLOC_ALIGN); 868 | assert(r != NULL); 869 | assert(l != r); 870 | buddy_free(buddy, l); 871 | buddy_free(buddy, r); 872 | memcpy(buddy_buf_control, buddy_buf, buddy_sizeof(size)); 873 | // double-free on a right node 874 | // that will propagate to a unallocated parent 875 | buddy_free(buddy, r); 876 | assert(memcmp(buddy_buf_control, buddy_buf, buddy_sizeof(size)) == 0); 877 | free(buddy_buf); 878 | free(buddy_buf_control); 879 | } 880 | 881 | void test_buddy_free_invalid_free_03(void) { 882 | size_t size = BUDDY_ALLOC_ALIGN * 2; 883 | unsigned char *buddy_buf = malloc(buddy_sizeof(size)); 884 | unsigned char *buddy_buf_control = malloc(buddy_sizeof(size)); 885 | unsigned char data_buf[4096]; 886 | struct buddy *buddy; 887 | void *r, *l, *m; 888 | START_TEST; 889 | buddy = buddy_init(buddy_buf, data_buf, size); 890 | l = buddy_malloc(buddy, BUDDY_ALLOC_ALIGN); 891 | assert(l != NULL); 892 | r = buddy_malloc(buddy, BUDDY_ALLOC_ALIGN); 893 | assert(r != NULL); 894 | assert(l != r); 895 | buddy_free(buddy, l); 896 | buddy_free(buddy, r); 897 | m = buddy_malloc(buddy, size); // full allocation 898 | assert(m == l); // at the start of the arena 899 | memcpy(buddy_buf_control, buddy_buf, buddy_sizeof(size)); 900 | // double-free on a right node 901 | // that will propagate to a fully-allocated parent 902 | buddy_free(buddy, r); 903 | assert(memcmp(buddy_buf_control, buddy_buf, buddy_sizeof(size)) == 0); 904 | free(buddy_buf); 905 | free(buddy_buf_control); 906 | } 907 | 908 | void test_buddy_free_invalid_free_04(void) { 909 | size_t size = BUDDY_ALLOC_ALIGN * 2; 910 | unsigned char *buddy_buf = malloc(buddy_sizeof(size)); 911 | unsigned char *buddy_buf_control = malloc(buddy_sizeof(size)); 912 | unsigned char data_buf[4096]; 913 | struct buddy *buddy; 914 | void *r, *l; 915 | START_TEST; 916 | buddy = buddy_init(buddy_buf, data_buf, size); 917 | l = buddy_malloc(buddy, BUDDY_ALLOC_ALIGN); 918 | assert(l != NULL); 919 | r = buddy_malloc(buddy, BUDDY_ALLOC_ALIGN); 920 | assert(r != NULL); 921 | assert(l != r); 922 | buddy_free(buddy, l); 923 | memcpy(buddy_buf_control, buddy_buf, buddy_sizeof(size)); 924 | // double-free on a left node 925 | // that will propagate to a partially-allocated parent 926 | buddy_free(buddy, l); 927 | /* the use check in buddy_tree_release handles this case */ 928 | assert(memcmp(buddy_buf_control, buddy_buf, buddy_sizeof(size)) == 0); 929 | free(buddy_buf); 930 | free(buddy_buf_control); 931 | } 932 | 933 | void test_buddy_safe_free_coverage(void) { 934 | unsigned char *buddy_buf = malloc(buddy_sizeof(4096)); 935 | unsigned char data_buf[4096]; 936 | struct buddy *buddy; 937 | START_TEST; 938 | buddy = buddy_init(buddy_buf, data_buf+1024, 1024); 939 | assert(buddy != NULL); 940 | assert(buddy_safe_free(NULL, NULL, 0) == BUDDY_SAFE_FREE_BUDDY_IS_NULL); 941 | assert(buddy_safe_free(buddy, NULL, 0) == BUDDY_SAFE_FREE_INVALID_ADDRESS); 942 | assert(buddy_safe_free(buddy, data_buf, 0) == BUDDY_SAFE_FREE_INVALID_ADDRESS); 943 | assert(buddy_safe_free(buddy, data_buf+2048, 0) == BUDDY_SAFE_FREE_INVALID_ADDRESS); 944 | assert(buddy_safe_free(buddy, data_buf+1024, 1024) == BUDDY_SAFE_FREE_INVALID_ADDRESS); 945 | free(buddy_buf); 946 | } 947 | 948 | void test_buddy_safe_free_alignment(void) { 949 | unsigned char *buddy_buf = malloc(buddy_sizeof(4096)); 950 | unsigned char data_buf[4096]; 951 | struct buddy *buddy; 952 | START_TEST; 953 | buddy = buddy_init(buddy_buf, data_buf, 4096); 954 | assert(buddy_safe_free(buddy, data_buf+1, 0) == BUDDY_SAFE_FREE_INVALID_ADDRESS); 955 | free(buddy_buf); 956 | } 957 | 958 | void test_buddy_safe_free_invalid_free_01(void) { 959 | size_t size = BUDDY_ALLOC_ALIGN * 2; 960 | unsigned char *buddy_buf = malloc(buddy_sizeof(size)); 961 | unsigned char *buddy_buf_control = malloc(buddy_sizeof(size)); 962 | unsigned char data_buf[4096]; 963 | struct buddy *buddy; 964 | void *r, *l; 965 | START_TEST; 966 | buddy = buddy_init(buddy_buf, data_buf, size); 967 | l = buddy_malloc(buddy, BUDDY_ALLOC_ALIGN); 968 | assert(l != NULL); 969 | r = buddy_malloc(buddy, BUDDY_ALLOC_ALIGN); 970 | assert(r != NULL); 971 | assert(l != r); 972 | assert(buddy_safe_free(buddy, r, BUDDY_ALLOC_ALIGN) == BUDDY_SAFE_FREE_SUCCESS); 973 | memcpy(buddy_buf_control, buddy_buf, buddy_sizeof(size)); 974 | // double-free on a right node 975 | // that will propagate to a partially-allocated parent 976 | assert(buddy_safe_free(buddy, r, BUDDY_ALLOC_ALIGN) == BUDDY_SAFE_FREE_INVALID_ADDRESS); 977 | assert(memcmp(buddy_buf_control, buddy_buf, buddy_sizeof(size)) == 0); 978 | free(buddy_buf); 979 | free(buddy_buf_control); 980 | } 981 | 982 | void test_buddy_safe_free_invalid_free_02(void) { 983 | size_t size = BUDDY_ALLOC_ALIGN * 2; 984 | unsigned char *buddy_buf = malloc(buddy_sizeof(size)); 985 | unsigned char *buddy_buf_control = malloc(buddy_sizeof(size)); 986 | unsigned char data_buf[4096]; 987 | struct buddy *buddy; 988 | void *r, *l; 989 | START_TEST; 990 | buddy = buddy_init(buddy_buf, data_buf, size); 991 | l = buddy_malloc(buddy, BUDDY_ALLOC_ALIGN); 992 | assert(l != NULL); 993 | r = buddy_malloc(buddy, BUDDY_ALLOC_ALIGN); 994 | assert(r != NULL); 995 | assert(l != r); 996 | assert(buddy_safe_free(buddy, l, BUDDY_ALLOC_ALIGN) == BUDDY_SAFE_FREE_SUCCESS); 997 | assert(buddy_safe_free(buddy, r, BUDDY_ALLOC_ALIGN) == BUDDY_SAFE_FREE_SUCCESS); 998 | memcpy(buddy_buf_control, buddy_buf, buddy_sizeof(size)); 999 | // double-free on a right node 1000 | // that will propagate to a unallocated parent 1001 | assert(buddy_safe_free(buddy, r, BUDDY_ALLOC_ALIGN) == BUDDY_SAFE_FREE_INVALID_ADDRESS); 1002 | assert(memcmp(buddy_buf_control, buddy_buf, buddy_sizeof(size)) == 0); 1003 | free(buddy_buf); 1004 | free(buddy_buf_control); 1005 | } 1006 | 1007 | void test_buddy_safe_free_invalid_free_03(void) { 1008 | size_t size = BUDDY_ALLOC_ALIGN * 2; 1009 | unsigned char *buddy_buf = malloc(buddy_sizeof(size)); 1010 | unsigned char *buddy_buf_control = malloc(buddy_sizeof(size)); 1011 | unsigned char data_buf[4096]; 1012 | struct buddy *buddy; 1013 | void *r, *l, *m; 1014 | START_TEST; 1015 | buddy = buddy_init(buddy_buf, data_buf, size); 1016 | l = buddy_malloc(buddy, BUDDY_ALLOC_ALIGN); 1017 | assert(l != NULL); 1018 | r = buddy_malloc(buddy, BUDDY_ALLOC_ALIGN); 1019 | assert(r != NULL); 1020 | assert(l != r); 1021 | assert(buddy_safe_free(buddy, l, BUDDY_ALLOC_ALIGN) == BUDDY_SAFE_FREE_SUCCESS); 1022 | assert(buddy_safe_free(buddy, r, BUDDY_ALLOC_ALIGN) == BUDDY_SAFE_FREE_SUCCESS); 1023 | m = buddy_malloc(buddy, size); // full allocation 1024 | assert(m == l); // at the start of the arena 1025 | memcpy(buddy_buf_control, buddy_buf, buddy_sizeof(size)); 1026 | // double-free on a right node 1027 | // that will propagate to a fully-allocated parent 1028 | assert(buddy_safe_free(buddy, r, BUDDY_ALLOC_ALIGN) == BUDDY_SAFE_FREE_INVALID_ADDRESS); 1029 | assert(memcmp(buddy_buf_control, buddy_buf, buddy_sizeof(size)) == 0); 1030 | free(buddy_buf); 1031 | free(buddy_buf_control); 1032 | } 1033 | 1034 | void test_buddy_safe_free_invalid_free_04(void) { 1035 | size_t size = BUDDY_ALLOC_ALIGN * 2; 1036 | unsigned char *buddy_buf = malloc(buddy_sizeof(size)); 1037 | unsigned char *buddy_buf_control = malloc(buddy_sizeof(size)); 1038 | unsigned char data_buf[4096]; 1039 | struct buddy *buddy; 1040 | void *r, *l; 1041 | START_TEST; 1042 | buddy = buddy_init(buddy_buf, data_buf, size); 1043 | l = buddy_malloc(buddy, BUDDY_ALLOC_ALIGN); 1044 | assert(l != NULL); 1045 | r = buddy_malloc(buddy, BUDDY_ALLOC_ALIGN); 1046 | assert(r != NULL); 1047 | assert(l != r); 1048 | assert(buddy_safe_free(buddy, l, BUDDY_ALLOC_ALIGN) == BUDDY_SAFE_FREE_SUCCESS); 1049 | memcpy(buddy_buf_control, buddy_buf, buddy_sizeof(size)); 1050 | // double-free on a left node 1051 | // that will propagate to a partially-allocated parent 1052 | assert(buddy_safe_free(buddy, l, BUDDY_ALLOC_ALIGN*2) == BUDDY_SAFE_FREE_INVALID_ADDRESS); 1053 | assert(memcmp(buddy_buf_control, buddy_buf, buddy_sizeof(size)) == 0); 1054 | free(buddy_buf); 1055 | free(buddy_buf_control); 1056 | } 1057 | 1058 | void test_buddy_safe_free_invalid_free_05(void) { 1059 | size_t size = BUDDY_ALLOC_ALIGN * 2; 1060 | unsigned char *buddy_buf = malloc(buddy_sizeof(size)); 1061 | unsigned char *buddy_buf_control = malloc(buddy_sizeof(size)); 1062 | unsigned char data_buf[4096]; 1063 | struct buddy *buddy; 1064 | void *l; 1065 | START_TEST; 1066 | buddy = buddy_init(buddy_buf, data_buf, size); 1067 | l = buddy_malloc(buddy, BUDDY_ALLOC_ALIGN); 1068 | memcpy(buddy_buf_control, buddy_buf, buddy_sizeof(size)); 1069 | // safe free with double size 1070 | assert(buddy_safe_free(buddy, l, BUDDY_ALLOC_ALIGN * 2) == BUDDY_SAFE_FREE_SIZE_MISMATCH); 1071 | assert(memcmp(buddy_buf_control, buddy_buf, buddy_sizeof(size)) == 0); 1072 | free(buddy_buf); 1073 | free(buddy_buf_control); 1074 | } 1075 | 1076 | void test_buddy_safe_free_invalid_free_06(void) { 1077 | size_t size = BUDDY_ALLOC_ALIGN * 2; 1078 | unsigned char *buddy_buf = malloc(buddy_sizeof(size)); 1079 | unsigned char *buddy_buf_control = malloc(buddy_sizeof(size)); 1080 | unsigned char data_buf[4096]; 1081 | struct buddy *buddy; 1082 | void *m; 1083 | START_TEST; 1084 | buddy = buddy_init(buddy_buf, data_buf, size); 1085 | m = buddy_malloc(buddy, BUDDY_ALLOC_ALIGN*2); 1086 | memcpy(buddy_buf_control, buddy_buf, buddy_sizeof(size)); 1087 | // safe free with half size 1088 | assert(buddy_safe_free(buddy, m, BUDDY_ALLOC_ALIGN) == BUDDY_SAFE_FREE_SIZE_MISMATCH); 1089 | assert(memcmp(buddy_buf_control, buddy_buf, buddy_sizeof(size)) == 0); 1090 | free(buddy_buf); 1091 | free(buddy_buf_control); 1092 | } 1093 | 1094 | void test_buddy_safe_free_invalid_free_07(void) { 1095 | size_t size = BUDDY_ALLOC_ALIGN * 2; 1096 | unsigned char *buddy_buf = malloc(buddy_sizeof(size)); 1097 | unsigned char *buddy_buf_control = malloc(buddy_sizeof(size)); 1098 | unsigned char data_buf[4096]; 1099 | struct buddy *buddy; 1100 | void *m; 1101 | START_TEST; 1102 | buddy = buddy_init(buddy_buf, data_buf, size); 1103 | m = buddy_malloc(buddy, BUDDY_ALLOC_ALIGN*2); 1104 | memcpy(buddy_buf_control, buddy_buf, buddy_sizeof(size)); 1105 | // safe free with zero size 1106 | assert(buddy_safe_free(buddy, m, 0) == BUDDY_SAFE_FREE_SIZE_MISMATCH); 1107 | assert(memcmp(buddy_buf_control, buddy_buf, buddy_sizeof(size)) == 0); 1108 | free(buddy_buf); 1109 | free(buddy_buf_control); 1110 | } 1111 | 1112 | void test_buddy_demo(void) { 1113 | size_t arena_size = 65536; 1114 | /* You need space for the metadata and for the arena */ 1115 | void *buddy_metadata = malloc(buddy_sizeof(arena_size)); 1116 | void *buddy_arena = malloc(arena_size); 1117 | struct buddy *buddy = buddy_init(buddy_metadata, buddy_arena, arena_size); 1118 | 1119 | /* Allocate using the buddy allocator */ 1120 | void *data = buddy_malloc(buddy, 2048); 1121 | /* Free using the buddy allocator */ 1122 | buddy_free(buddy, data); 1123 | 1124 | assert(buddy_is_empty(NULL) == false); 1125 | assert(buddy_is_empty(buddy)); 1126 | 1127 | free(buddy_metadata); 1128 | free(buddy_arena); 1129 | } 1130 | 1131 | void test_buddy_demo_embedded(void) { 1132 | size_t arena_size = 65536; 1133 | /* You need space for arena and builtin metadata */ 1134 | void *buddy_arena = malloc(arena_size); 1135 | struct buddy *buddy = buddy_embed(buddy_arena, arena_size); 1136 | 1137 | /* Allocate using the buddy allocator */ 1138 | void *data = buddy_malloc(buddy, 2048); 1139 | /* Free using the buddy allocator */ 1140 | buddy_free(buddy, data); 1141 | 1142 | free(buddy_arena); 1143 | } 1144 | 1145 | void test_buddy_calloc(void) { 1146 | unsigned char *buddy_buf = malloc(buddy_sizeof(4096)); 1147 | unsigned char data_buf[4096]; 1148 | struct buddy *buddy; 1149 | char *result; 1150 | START_TEST; 1151 | memset(data_buf, 1, 4096); 1152 | buddy = buddy_init(buddy_buf, data_buf, 4096); 1153 | result = buddy_calloc(buddy, sizeof(char), 4096); 1154 | for (size_t i = 0; i < 4096; i++) { 1155 | assert(result[i] == 0); 1156 | } 1157 | free(buddy_buf); 1158 | } 1159 | 1160 | void test_buddy_calloc_no_members(void) { 1161 | unsigned char *buddy_buf = malloc(buddy_sizeof(4096)); 1162 | unsigned char data_buf[4096]; 1163 | struct buddy *buddy; 1164 | char *result; 1165 | START_TEST; 1166 | buddy = buddy_init(buddy_buf, data_buf, 4096); 1167 | result = buddy_calloc(buddy, 0, 4096); 1168 | /* This is implementation-defined in the standard! */ 1169 | assert(result != NULL); 1170 | free(buddy_buf); 1171 | } 1172 | 1173 | void test_buddy_calloc_no_size(void) { 1174 | unsigned char *buddy_buf = malloc(buddy_sizeof(4096)); 1175 | unsigned char data_buf[4096]; 1176 | struct buddy *buddy; 1177 | char *result; 1178 | START_TEST; 1179 | buddy = buddy_init(buddy_buf, data_buf, 4096); 1180 | result = buddy_calloc(buddy, sizeof(char), 0); 1181 | /* This is implementation-defined in the standard! */ 1182 | assert(result != NULL); 1183 | free(buddy_buf); 1184 | } 1185 | 1186 | void test_buddy_calloc_overflow(void) { 1187 | unsigned char *buddy_buf = malloc(buddy_sizeof(4096)); 1188 | unsigned char data_buf[4096]; 1189 | struct buddy *buddy; 1190 | char *result; 1191 | START_TEST; 1192 | buddy = buddy_init(buddy_buf, data_buf, 4096); 1193 | result = buddy_calloc(buddy, sizeof(short), SIZE_MAX); 1194 | assert(result == NULL); 1195 | free(buddy_buf); 1196 | } 1197 | 1198 | void test_buddy_realloc_01(void) { 1199 | unsigned char *buddy_buf = malloc(buddy_sizeof(4096)); 1200 | unsigned char data_buf[4096]; 1201 | struct buddy *buddy; 1202 | void *result; 1203 | START_TEST; 1204 | buddy = buddy_init(buddy_buf, data_buf, 4096); 1205 | assert(buddy != NULL); 1206 | /* This is implementation-defined! */ 1207 | result = buddy_realloc(buddy, NULL, 0, false); 1208 | assert(result != NULL); 1209 | free(buddy_buf); 1210 | } 1211 | 1212 | void test_buddy_realloc_02(void) { 1213 | unsigned char *buddy_buf = malloc(buddy_sizeof(4096)); 1214 | unsigned char data_buf[4096]; 1215 | struct buddy *buddy; 1216 | void *result; 1217 | START_TEST; 1218 | buddy = buddy_init(buddy_buf, data_buf, 4096); 1219 | assert(buddy != NULL); 1220 | result = buddy_realloc(buddy, NULL, 128, false); 1221 | assert(result == data_buf); 1222 | free(buddy_buf); 1223 | } 1224 | 1225 | void test_buddy_realloc_03(void) { 1226 | unsigned char *buddy_buf = malloc(buddy_sizeof(4096)); 1227 | unsigned char data_buf[4096]; 1228 | struct buddy *buddy; 1229 | void *result; 1230 | START_TEST; 1231 | buddy = buddy_init(buddy_buf, data_buf, 4096); 1232 | assert(buddy != NULL); 1233 | result = buddy_realloc(buddy, NULL, 128, false); 1234 | assert(result == data_buf); 1235 | result = buddy_realloc(buddy, result, 128, false); 1236 | assert(result == data_buf); 1237 | free(buddy_buf); 1238 | } 1239 | 1240 | void test_buddy_realloc_04(void) { 1241 | unsigned char *buddy_buf = malloc(buddy_sizeof(4096)); 1242 | unsigned char data_buf[4096]; 1243 | struct buddy *buddy; 1244 | void *result; 1245 | START_TEST; 1246 | buddy = buddy_init(buddy_buf, data_buf, 4096); 1247 | assert(buddy != NULL); 1248 | result = buddy_realloc(buddy, NULL, 128, false); 1249 | assert(result == data_buf); 1250 | result = buddy_realloc(buddy, result, 64, false); 1251 | assert(result == data_buf); 1252 | free(buddy_buf); 1253 | } 1254 | 1255 | void test_buddy_realloc_05(void) { 1256 | unsigned char *buddy_buf = malloc(buddy_sizeof(512)); 1257 | unsigned char data_buf[512]; 1258 | struct buddy *buddy; 1259 | void *result; 1260 | START_TEST; 1261 | buddy = buddy_init(buddy_buf, data_buf, 512); 1262 | assert(buddy != NULL); 1263 | result = buddy_realloc(buddy, NULL, 128, false); 1264 | assert(result == data_buf); 1265 | result = buddy_realloc(buddy, result, 256, false); 1266 | assert(result == data_buf); 1267 | free(buddy_buf); 1268 | } 1269 | 1270 | void test_buddy_realloc_06(void) { 1271 | unsigned char *buddy_buf = malloc(buddy_sizeof(512)); 1272 | unsigned char data_buf[512]; 1273 | struct buddy *buddy; 1274 | void *result; 1275 | START_TEST; 1276 | buddy = buddy_init(buddy_buf, data_buf, 512); 1277 | assert(buddy != NULL); 1278 | result = buddy_realloc(buddy, NULL, 128, false); 1279 | assert(result == data_buf); 1280 | result = buddy_realloc(buddy, result, 0, false); 1281 | assert(result == NULL); 1282 | free(buddy_buf); 1283 | } 1284 | 1285 | void test_buddy_realloc_07(void) { 1286 | unsigned char *buddy_buf = malloc(buddy_sizeof(512)); 1287 | unsigned char data_buf[512]; 1288 | struct buddy *buddy; 1289 | void *result; 1290 | START_TEST; 1291 | buddy = buddy_init(buddy_buf, data_buf, 512); 1292 | assert(buddy != NULL); 1293 | result = buddy_realloc(buddy, NULL, 128, false); 1294 | assert(result == data_buf); 1295 | result = buddy_realloc(buddy, result, 1024, false); 1296 | assert(result == NULL); 1297 | free(buddy_buf); 1298 | } 1299 | 1300 | void test_buddy_realloc_08(void) { 1301 | unsigned char *buddy_buf = malloc(buddy_sizeof(512)); 1302 | unsigned char data_buf[512]; 1303 | struct buddy *buddy; 1304 | void *result; 1305 | START_TEST; 1306 | buddy = buddy_init(buddy_buf, data_buf, 512); 1307 | assert(buddy != NULL); 1308 | assert(buddy_malloc(buddy, 256) == data_buf); 1309 | result = buddy_realloc(buddy, NULL, 256, false); 1310 | assert(result == data_buf + 256); 1311 | result = buddy_realloc(buddy, result, 512, false); 1312 | assert(result == NULL); 1313 | free(buddy_buf); 1314 | } 1315 | 1316 | void test_buddy_realloc_alignment(void) { 1317 | unsigned char *buddy_buf = malloc(buddy_sizeof(4096)); 1318 | unsigned char data_buf[4096]; 1319 | struct buddy *buddy; 1320 | START_TEST; 1321 | buddy = buddy_init(buddy_buf, data_buf, 4096); 1322 | assert(buddy_realloc(buddy, data_buf+1, 2048, false) == NULL); 1323 | free(buddy_buf); 1324 | } 1325 | 1326 | void test_buddy_realloc_ignore_01(void) { 1327 | unsigned char *buddy_buf = malloc(buddy_sizeof(4096)); 1328 | unsigned char data_buf[4096]; 1329 | struct buddy *buddy; 1330 | void *result; 1331 | unsigned char *ba; 1332 | START_TEST; 1333 | memset(data_buf, 0, 4096); /* make sure the buffer is empty */ 1334 | buddy = buddy_init(buddy_buf, data_buf, 4096); 1335 | buddy_malloc(buddy, 64); /* allocate one slot */ 1336 | result = buddy_malloc(buddy, 64); /* allocate its sibling */ 1337 | memset(result, 255, 64); /* put some data in it */ 1338 | result = buddy_realloc(buddy, result, 128, true); /* get a new slot, ignore data */ 1339 | ba = (unsigned char *) result; 1340 | for (size_t i = 0; i < 64; i++) { 1341 | assert(ba[i] == 0); 1342 | } 1343 | free(buddy_buf); 1344 | } 1345 | 1346 | void test_buddy_reallocarray_01(void) { 1347 | unsigned char *buddy_buf = malloc(buddy_sizeof(512)); 1348 | unsigned char data_buf[512]; 1349 | struct buddy *buddy; 1350 | void *result; 1351 | START_TEST; 1352 | buddy = buddy_init(buddy_buf, data_buf, 512); 1353 | result = buddy_reallocarray(buddy, NULL, 0, 0, false); 1354 | /* This is implementation-defined! */ 1355 | assert(result != NULL); 1356 | free(buddy_buf); 1357 | } 1358 | 1359 | void test_buddy_reallocarray_02(void) { 1360 | unsigned char *buddy_buf = malloc(buddy_sizeof(512)); 1361 | unsigned char data_buf[512]; 1362 | struct buddy *buddy; 1363 | void *result; 1364 | START_TEST; 1365 | buddy = buddy_init(buddy_buf, data_buf, 512); 1366 | result = buddy_reallocarray(buddy, NULL, sizeof(short), SIZE_MAX, false); 1367 | assert(result == NULL); 1368 | free(buddy_buf); 1369 | } 1370 | 1371 | void test_buddy_reallocarray_03(void) { 1372 | unsigned char *buddy_buf = malloc(buddy_sizeof(512)); 1373 | unsigned char data_buf[512]; 1374 | struct buddy *buddy; 1375 | void *result; 1376 | START_TEST; 1377 | buddy = buddy_init(buddy_buf, data_buf, 512); 1378 | result = buddy_reallocarray(buddy, NULL, sizeof(char), 256, false); 1379 | assert(result == data_buf); 1380 | free(buddy_buf); 1381 | } 1382 | 1383 | void test_buddy_embedded_not_enough_memory(void) { 1384 | unsigned char buf[4]; 1385 | START_TEST; 1386 | assert(buddy_embed(buf, 4) == NULL); 1387 | assert(buddy_embed(buf, 0) == NULL); 1388 | } 1389 | 1390 | void test_buddy_embedded_null(void) { 1391 | struct buddy *buddy; 1392 | START_TEST; 1393 | buddy = buddy_embed(NULL, 4096); 1394 | assert(buddy == NULL); 1395 | } 1396 | 1397 | void test_buddy_embedded_01(void) { 1398 | unsigned char buf[4096]; 1399 | struct buddy *buddy; 1400 | START_TEST; 1401 | buddy = buddy_embed(buf, 4096); 1402 | assert(buddy != NULL); 1403 | } 1404 | 1405 | void test_buddy_embedded_malloc_01(void) { 1406 | unsigned char buf[4096]; 1407 | struct buddy *buddy; 1408 | START_TEST; 1409 | buddy = buddy_embed(buf, 4096); 1410 | assert(buddy != NULL); 1411 | assert(buddy_malloc(buddy, 2048) == buf); 1412 | assert(buddy_malloc(buddy, 2048) == NULL); 1413 | buddy_free(buddy, buf); 1414 | assert(buddy_malloc(buddy, 2048) == buf); 1415 | assert(buddy_malloc(buddy, 2048) == NULL); 1416 | } 1417 | 1418 | void test_buddy_embedded_malloc_alignment(void) { 1419 | unsigned char buf[4096]; 1420 | struct buddy *buddy; 1421 | START_TEST; 1422 | buddy = buddy_embed_alignment(buf, 4096, 0); 1423 | assert(buddy == NULL); 1424 | buddy = buddy_embed_alignment(buf, 4096, 4096); 1425 | assert(buddy == NULL); 1426 | buddy = buddy_embed_alignment(buf, 4096, 2048); 1427 | assert(buddy_arena_size(buddy) == 2048); 1428 | buddy = buddy_embed_alignment(buf, 4096, 1024); 1429 | assert(buddy_arena_size(buddy) == 3072); 1430 | buddy = buddy_embed_alignment(buf, 4096, 512); 1431 | assert(buddy_arena_size(buddy) == 3584); 1432 | } 1433 | 1434 | void test_buddy_embed_at(void) { 1435 | unsigned char buf1[4096] = {0}; 1436 | unsigned char buf2[4096] = {0}; 1437 | struct buddy* buddy; 1438 | START_TEST; 1439 | buddy = buddy_embed(buf1, 4096); 1440 | buddy_malloc(buddy, 2048); 1441 | memcpy(buf2, buf1, 4096); 1442 | buddy = buddy_get_embed_at(buf2, 0); 1443 | assert(buddy == NULL); 1444 | buddy = buddy_get_embed_at(buf2, 4096); 1445 | assert(buddy != NULL); 1446 | assert(buddy_malloc(buddy, 2048) == NULL); 1447 | buddy_free(buddy, buf2); 1448 | assert(buddy_malloc(buddy, 2048) == buf2); 1449 | } 1450 | 1451 | void test_buddy_mixed_use_01(void) { 1452 | unsigned char *buddy_buf = malloc(buddy_sizeof(512)); 1453 | unsigned char data_buf[512]; 1454 | struct buddy *buddy; 1455 | void *addr[8] = {0}; 1456 | START_TEST; 1457 | buddy = buddy_init(buddy_buf, data_buf, 512); 1458 | for (size_t i = 0; i < 8; i++) { 1459 | addr[i] = buddy_malloc(buddy, 64); 1460 | assert(addr[i] != NULL); 1461 | } 1462 | for (size_t i = 0; i < 8; i++) { 1463 | if (i%2 == 0) { 1464 | buddy_free(buddy, addr[i]); 1465 | } 1466 | } 1467 | assert(buddy_malloc(buddy, 64) != NULL); 1468 | assert(buddy_malloc(buddy, 64) != NULL); 1469 | assert(buddy_malloc(buddy, 64) != NULL); 1470 | assert(buddy_malloc(buddy, 64) != NULL); 1471 | assert(buddy_malloc(buddy, 64) == NULL); 1472 | free(buddy_buf); 1473 | } 1474 | 1475 | void test_buddy_mixed_use_02(void) { 1476 | unsigned char *buddy_buf = malloc(buddy_sizeof(512)); 1477 | unsigned char data_buf[512]; 1478 | struct buddy *buddy; 1479 | void *addr[8] = {0}; 1480 | START_TEST; 1481 | buddy = buddy_init(buddy_buf, data_buf, 512); 1482 | for (size_t i = 0; i < 8; i++) { 1483 | addr[i] = buddy_malloc(buddy, 64); 1484 | assert(addr[i] != NULL); 1485 | } 1486 | for (size_t i = 0; i < 8; i++) { 1487 | buddy_free(buddy, addr[i]); 1488 | } 1489 | assert(buddy_malloc(buddy, 256) != NULL); 1490 | assert(buddy_malloc(buddy, 128) != NULL); 1491 | assert(buddy_malloc(buddy, 64) != NULL); 1492 | assert(buddy_malloc(buddy, 64) != NULL); 1493 | assert(buddy_malloc(buddy, 64) == NULL); 1494 | free(buddy_buf); 1495 | } 1496 | 1497 | void test_buddy_mixed_use_03(void) { 1498 | unsigned char *buddy_buf = malloc(buddy_sizeof(512)); 1499 | unsigned char data_buf[512]; 1500 | struct buddy *buddy; 1501 | void *addr[4] = {0}; 1502 | START_TEST; 1503 | buddy = buddy_init(buddy_buf, data_buf, 512); 1504 | for (size_t i = 0; i < 4; i++) { 1505 | addr[i] = buddy_malloc(buddy, 128); 1506 | assert(addr[i] != NULL); 1507 | } 1508 | for (size_t i = 0; i < 4; i++) { 1509 | buddy_free(buddy, addr[i]); 1510 | } 1511 | assert(buddy_malloc(buddy, 256) != NULL); 1512 | assert(buddy_malloc(buddy, 256) != NULL); 1513 | assert(buddy_malloc(buddy, 256) == NULL); 1514 | free(buddy_buf); 1515 | } 1516 | 1517 | void test_buddy_large_arena(void) { 1518 | size_t size = PSS(2147483648) /* 2/1 GB */; 1519 | unsigned char *data_buf = malloc(size); 1520 | unsigned char *buddy_buf = malloc(buddy_sizeof(size)); 1521 | struct buddy *buddy; 1522 | START_TEST; 1523 | buddy = buddy_init(buddy_buf, data_buf, size); 1524 | assert(buddy != NULL); 1525 | assert(buddy_malloc(buddy, size) == data_buf); 1526 | buddy_free(buddy, data_buf); 1527 | free(buddy_buf); 1528 | free(data_buf); 1529 | } 1530 | 1531 | void *walker_01(void *ctx, void *addr, size_t size, size_t allocated) { 1532 | size_t *counter = (size_t *) ctx; 1533 | (void) addr; 1534 | assert(size == 64); 1535 | assert(allocated); 1536 | (*counter)++; 1537 | if ((*counter) > 2) { 1538 | return addr; /* cause a stop */ 1539 | } 1540 | return NULL; 1541 | } 1542 | 1543 | void test_buddy_walk_01(void) { 1544 | unsigned char *buddy_buf = malloc(buddy_sizeof(512)); 1545 | unsigned char data_buf[512]; 1546 | struct buddy *buddy; 1547 | void *a; 1548 | size_t counter; 1549 | START_TEST; 1550 | buddy = buddy_init(buddy_buf, data_buf, 512); 1551 | assert(buddy_walk(NULL, walker_01, NULL) == NULL); 1552 | assert(buddy_walk(buddy, NULL, NULL) == NULL); 1553 | a = buddy_malloc(buddy, 64); 1554 | buddy_malloc(buddy, 64); 1555 | counter = 0; 1556 | assert(buddy_walk(buddy, walker_01, &counter) == NULL); 1557 | assert(counter == 2); 1558 | buddy_free(buddy, a); 1559 | counter = 0; 1560 | assert(buddy_walk(buddy, walker_01, &counter) == NULL); 1561 | assert(counter == 1); 1562 | counter = 0; 1563 | buddy_malloc(buddy, 64); 1564 | buddy_malloc(buddy, 64); 1565 | buddy_malloc(buddy, 64); 1566 | assert(buddy_walk(buddy, walker_01, &counter) != NULL); 1567 | free(buddy_buf); 1568 | } 1569 | 1570 | void *walker_02(void *ctx, void *addr, size_t size, size_t allocated) { 1571 | size_t *counter = (size_t *) ctx; 1572 | (void) addr; 1573 | assert(size == 128); 1574 | assert(allocated); 1575 | (*counter)++; 1576 | if ((*counter) > 2) { 1577 | return addr; /* cause a stop */ 1578 | } 1579 | return NULL; 1580 | } 1581 | void test_buddy_walk_02(void) { 1582 | unsigned char *buddy_buf = malloc(buddy_sizeof(512)); 1583 | unsigned char data_buf[512]; 1584 | struct buddy *buddy; 1585 | size_t counter; 1586 | START_TEST; 1587 | buddy = buddy_init(buddy_buf, data_buf, 512); 1588 | counter = 0; 1589 | buddy_malloc(buddy, 128); 1590 | buddy_malloc(buddy, 128); 1591 | buddy_malloc(buddy, 128); 1592 | assert(buddy_walk(buddy, walker_02, &counter) != NULL); 1593 | free(buddy_buf); 1594 | } 1595 | 1596 | struct walker_03_entry { 1597 | void *addr; 1598 | size_t size; 1599 | }; 1600 | 1601 | void *walker_03(void *ctx, void *addr, size_t size, size_t allocated) { 1602 | struct walker_03_entry (*context) = (struct walker_03_entry *) ctx; 1603 | unsigned int found = 0; 1604 | for (size_t i = 0; i < 3; i++) { 1605 | struct walker_03_entry *current = context + i; 1606 | if ((current->addr == addr) && (current->size) == size) { 1607 | found = 1; 1608 | break; 1609 | } 1610 | } 1611 | assert(found); 1612 | (void) allocated; 1613 | return NULL; 1614 | } 1615 | 1616 | void test_buddy_walk_03(void) { 1617 | unsigned char *buddy_buf = malloc(buddy_sizeof(512)); 1618 | unsigned char data_buf[512]; 1619 | struct buddy *buddy; 1620 | struct walker_03_entry context[3]; 1621 | START_TEST; 1622 | buddy = buddy_init(buddy_buf, data_buf, 512); 1623 | context[0] = (struct walker_03_entry){.addr = buddy_malloc(buddy, 128), .size = 128}; 1624 | context[1] = (struct walker_03_entry){.addr = buddy_malloc(buddy, 64), .size = 64}; 1625 | context[2] = (struct walker_03_entry){.addr = buddy_malloc(buddy, 256), .size = 256}; 1626 | assert(buddy_walk(buddy, walker_03, &context) == NULL); 1627 | free(buddy_buf); 1628 | } 1629 | 1630 | void *walker_04(void *ctx, void *addr, size_t size, size_t allocated) { 1631 | struct buddy *buddy = (struct buddy *) ctx; 1632 | assert(addr != NULL); 1633 | assert(size != 0); 1634 | assert(allocated); 1635 | buddy_free(buddy, addr); 1636 | return NULL; 1637 | } 1638 | 1639 | void test_buddy_walk_04(void) { 1640 | unsigned char *buddy_buf = malloc(buddy_sizeof(512)); 1641 | unsigned char data_buf[512]; 1642 | struct buddy *buddy; 1643 | START_TEST; 1644 | buddy = buddy_init(buddy_buf, data_buf, 512); 1645 | buddy_malloc(buddy, 128); 1646 | buddy_malloc(buddy, 64); 1647 | buddy_malloc(buddy, 256); 1648 | assert(buddy_walk(buddy, walker_04, buddy) == NULL); 1649 | assert(buddy_is_empty(buddy)); 1650 | free(buddy_buf); 1651 | } 1652 | 1653 | void *walker_05(void *ctx, void *addr, size_t size, size_t allocated) { 1654 | (void) addr; 1655 | (void) size; 1656 | (void) allocated; 1657 | return ctx; 1658 | } 1659 | 1660 | void test_buddy_walk_05(void) { 1661 | unsigned char *buddy_buf = malloc(buddy_sizeof(4096)); 1662 | unsigned char data_buf[4096]; 1663 | struct buddy *buddy; 1664 | unsigned int ctx = 0; 1665 | START_TEST; 1666 | buddy = buddy_init(buddy_buf, data_buf, 3648); // virtual slots 1667 | assert(buddy_walk(buddy, walker_05, &ctx) == NULL); 1668 | assert(walker_05(&ctx, NULL, 0, 1) == &ctx); // coverage 1669 | free(buddy_buf); 1670 | } 1671 | 1672 | void *walker_06(void *ctx, void *addr, size_t size, size_t allocated) { 1673 | struct buddy *buddy = (struct buddy *) ctx; 1674 | assert(allocated); 1675 | buddy_realloc(buddy, addr, size, false); 1676 | return NULL; 1677 | } 1678 | 1679 | void test_buddy_walk_06(void) { 1680 | unsigned char *buddy_buf = malloc(buddy_sizeof(512)); 1681 | unsigned char data_buf[512]; 1682 | struct buddy *buddy; 1683 | void *a[8]; 1684 | START_TEST; 1685 | buddy = buddy_init(buddy_buf, data_buf, 512); 1686 | a[0] = buddy_malloc(buddy, 64); 1687 | a[1] = buddy_malloc(buddy, 64); 1688 | a[2] = buddy_malloc(buddy, 64); 1689 | a[3] = buddy_malloc(buddy, 64); 1690 | a[4] = buddy_malloc(buddy, 64); 1691 | a[5] = buddy_malloc(buddy, 64); 1692 | a[6] = buddy_malloc(buddy, 64); 1693 | a[7] = buddy_malloc(buddy, 64); 1694 | buddy_free(buddy, a[1]); 1695 | buddy_free(buddy, a[3]); 1696 | buddy_free(buddy, a[5]); 1697 | buddy_free(buddy, a[7]); 1698 | // at this point we have 4x64 slots free but they are 1699 | // fragmented and cannot be coalesced 1700 | assert(buddy_malloc(buddy, 256) == NULL); 1701 | buddy_walk(buddy, walker_06, buddy); 1702 | assert(buddy_malloc(buddy, 256) != NULL); 1703 | free(buddy_buf); 1704 | } 1705 | 1706 | void test_buddy_reserve_01(void) { 1707 | unsigned char *buddy_buf = malloc(buddy_sizeof(512)); 1708 | unsigned char data_buf[512]; 1709 | struct buddy *buddy; 1710 | START_TEST; 1711 | buddy = buddy_init(buddy_buf, data_buf, 512); 1712 | buddy_reserve_range(buddy, buddy_buf, 512); // wrong dst 1713 | assert(buddy_malloc(buddy, 512) == data_buf); // entire buddy should be free 1714 | free(buddy_buf); 1715 | } 1716 | 1717 | void test_buddy_reserve_02(void) { 1718 | unsigned char *buddy_buf = malloc(buddy_sizeof(512)); 1719 | unsigned char data_buf[512]; 1720 | struct buddy *buddy; 1721 | START_TEST; 1722 | buddy = buddy_init(buddy_buf, data_buf, 512); 1723 | buddy_reserve_range(buddy, data_buf, 0); // zero length 1724 | assert(buddy_malloc(buddy, 512) == data_buf); // entire buddy should be free 1725 | free(buddy_buf); 1726 | } 1727 | 1728 | void test_buddy_reserve_03(void) { 1729 | unsigned char *buddy_buf = malloc(buddy_sizeof(512)); 1730 | unsigned char data_buf[512]; 1731 | struct buddy *buddy; 1732 | START_TEST; 1733 | buddy = buddy_init(buddy_buf, data_buf, 512); 1734 | buddy_reserve_range(buddy, data_buf, 512); // full length 1735 | assert(buddy_malloc(buddy, 512) == NULL); // entire buddy should be busy 1736 | free(buddy_buf); 1737 | } 1738 | 1739 | void test_buddy_reserve_04(void) { 1740 | unsigned char *buddy_buf = malloc(buddy_sizeof(512)); 1741 | unsigned char data_buf[512]; 1742 | struct buddy *buddy; 1743 | START_TEST; 1744 | buddy = buddy_init(buddy_buf, data_buf, 512); 1745 | buddy_reserve_range(buddy, data_buf, 512-16); // almost-full length 1746 | assert(buddy_malloc(buddy, 512) == NULL); // entire buddy should be busy 1747 | free(buddy_buf); 1748 | } 1749 | 1750 | void test_buddy_reserve_05(void) { 1751 | unsigned char *buddy_buf = malloc(buddy_sizeof(512)); 1752 | unsigned char data_buf[512]; 1753 | struct buddy *buddy; 1754 | START_TEST; 1755 | buddy = buddy_init(buddy_buf, data_buf, 512); 1756 | buddy_reserve_range(buddy, data_buf, 256); // half length 1757 | assert(buddy_malloc(buddy, 512) == NULL); 1758 | assert(buddy_malloc(buddy, 256) == (data_buf + 256)); 1759 | assert(buddy_malloc(buddy, 256) == NULL); 1760 | free(buddy_buf); 1761 | } 1762 | 1763 | void test_buddy_reserve_coverage(void) { 1764 | unsigned char *buddy_buf = malloc(buddy_sizeof(512)); 1765 | unsigned char data_buf[512]; 1766 | struct buddy *buddy; 1767 | START_TEST; 1768 | buddy = buddy_init(buddy_buf, data_buf, 512); 1769 | buddy_reserve_range(NULL, NULL, 0); // coverage-only 1770 | buddy_reserve_range(buddy, NULL, 0); // coverage-only 1771 | free(buddy_buf); 1772 | } 1773 | 1774 | void test_buddy_unsafe_release_01(void) { 1775 | unsigned char *buddy_buf = malloc(buddy_sizeof(512)); 1776 | unsigned char data_buf[512]; 1777 | struct buddy *buddy; 1778 | START_TEST; 1779 | buddy = buddy_init(buddy_buf, data_buf, 512); 1780 | buddy_malloc(buddy, 512); // Use all 1781 | buddy_unsafe_release_range(buddy, data_buf, 0); // Zero length 1782 | assert(buddy_malloc(buddy, 512) == NULL); // no release should have happened 1783 | free(buddy_buf); 1784 | } 1785 | 1786 | void test_buddy_unsafe_release_02(void) { 1787 | unsigned char *buddy_buf = malloc(buddy_sizeof(512)); 1788 | unsigned char data_buf[512]; 1789 | struct buddy *buddy; 1790 | START_TEST; 1791 | buddy = buddy_init(buddy_buf, data_buf, 512); 1792 | buddy_reserve_range(buddy, data_buf, 256); // half length 1793 | buddy_malloc(buddy, 256); // Use the rest 1794 | assert(buddy_malloc(buddy, 256) == NULL); // no space 1795 | buddy_unsafe_release_range(buddy, data_buf, 256); // get back half 1796 | assert(buddy_malloc(buddy, 256) == data_buf); // there should be space now 1797 | free(buddy_buf); 1798 | } 1799 | 1800 | void test_buddy_fragmentation(void) { 1801 | size_t buddy_size = PSS(256); 1802 | void *ptrs[4]; 1803 | unsigned char *buddy_buf = malloc(buddy_sizeof(buddy_size)); 1804 | unsigned char *data_buf = malloc(buddy_size); 1805 | struct buddy *b = buddy_init(buddy_buf, data_buf, buddy_size); 1806 | 1807 | START_TEST; 1808 | 1809 | // This test verified the same as test_buddy_tree_fragmentation, 1810 | // just through the allocator public interface 1811 | 1812 | // No fragmentation for invalid allocator 1813 | assert(buddy_fragmentation(NULL) == 0); 1814 | 1815 | // No fragmentation for empty allocator 1816 | assert(buddy_fragmentation(b) == 0); 1817 | 1818 | // No fragmentation for full allocator either 1819 | buddy_malloc(b, buddy_size); 1820 | assert(buddy_fragmentation(b) == 0); 1821 | buddy_free(b, data_buf); 1822 | 1823 | // Some fragmentation for partially-used allocator 1824 | buddy_malloc(b, PSS(64)); 1825 | assert(buddy_fragmentation(b) == 143); 1826 | 1827 | buddy_free(b, data_buf); 1828 | for (size_t i = 0; i < 4; i++){ 1829 | ptrs[i] = buddy_malloc(b, PSS(64)); 1830 | } 1831 | buddy_free(b, ptrs[0]); 1832 | buddy_free(b, ptrs[2]); 1833 | assert(buddy_fragmentation(b) == 191); 1834 | 1835 | free(buddy_buf); 1836 | free(data_buf); 1837 | } 1838 | 1839 | void test_buddy_is_empty(void) { 1840 | size_t buddy_size = 1024; 1841 | unsigned char *buddy_buf = malloc(buddy_sizeof(buddy_size)); 1842 | unsigned char *data_buf = malloc(buddy_size); 1843 | struct buddy *buddy; 1844 | START_TEST; 1845 | buddy = buddy_init(buddy_buf, data_buf, buddy_size); 1846 | assert(buddy_is_empty(buddy)); 1847 | free(buddy_buf); 1848 | free(data_buf); 1849 | } 1850 | 1851 | void test_buddy_is_full(void) { 1852 | size_t buddy_size = 1024; 1853 | unsigned char *buddy_buf = malloc(buddy_sizeof(buddy_size)); 1854 | unsigned char *data_buf = malloc(buddy_size); 1855 | struct buddy *buddy; 1856 | START_TEST; 1857 | buddy = buddy_init(buddy_buf, data_buf, buddy_size); 1858 | buddy_malloc(buddy, 1024); 1859 | assert(buddy_is_full(buddy)); 1860 | free(buddy_buf); 1861 | free(data_buf); 1862 | assert(buddy_is_full(NULL) == false); 1863 | } 1864 | 1865 | void test_buddy_slot_alignment(void) { 1866 | size_t arena_size = 4096; 1867 | size_t max_alignment = 4096; 1868 | size_t slot_alignment; 1869 | void *arena, *alloc, *slot; 1870 | struct buddy *buddy; 1871 | START_TEST; 1872 | arena = my_aligned_malloc(arena_size, max_alignment); 1873 | for (size_t alignment = 1; alignment <= max_alignment; alignment <<= 1) { 1874 | alloc = malloc(buddy_sizeof_alignment(arena_size, alignment)); 1875 | buddy = buddy_init_alignment(alloc, arena, arena_size, alignment); 1876 | 1877 | for(size_t i = 0; i < (arena_size/alignment); i++) { 1878 | slot = buddy_malloc(buddy, alignment); 1879 | slot_alignment = ((uintptr_t)slot) % alignment; 1880 | assert(slot_alignment == 0); 1881 | } 1882 | 1883 | assert(buddy_arena_free_size(buddy) == 0); 1884 | free(alloc); 1885 | } 1886 | my_aligned_free(arena); 1887 | } 1888 | 1889 | void test_buddy_invalid_slot_alignment(void) { 1890 | size_t arena_size = 4096; 1891 | unsigned char arena[4096]; 1892 | unsigned char buddy[4096]; 1893 | START_TEST; 1894 | assert(buddy_sizeof_alignment(arena_size, 0) == 0); 1895 | assert(buddy_sizeof_alignment(arena_size, 3) == 0); 1896 | assert(buddy_init_alignment(buddy, arena, 4096, 0) == NULL); 1897 | assert(buddy_init_alignment(buddy, arena, 4096, 3) == NULL); 1898 | assert(buddy_embed_alignment(arena, 4096, 0) == NULL); 1899 | assert(buddy_embed_alignment(arena, 4096, 3) == NULL); 1900 | } 1901 | 1902 | struct buddy_change_tracker_context { 1903 | size_t total_calls; 1904 | }; 1905 | 1906 | void buddy_change_tracker_cb(void* context, unsigned char* addr, size_t length) { 1907 | struct buddy_change_tracker_context *tracker_context = (struct buddy_change_tracker_context *) context; 1908 | (void) addr; 1909 | (void) length; 1910 | tracker_context->total_calls++; 1911 | } 1912 | 1913 | void test_buddy_change_tracking() { 1914 | struct buddy_change_tracker_context context = {0}; 1915 | unsigned char arena[4096] = {0}; 1916 | struct buddy *buddy = buddy_embed(arena, 4096); 1917 | void *slot; 1918 | START_TEST; 1919 | buddy_enable_change_tracking(buddy, &context, buddy_change_tracker_cb); 1920 | assert(context.total_calls == 0); 1921 | slot = buddy_malloc(buddy, 512); 1922 | assert(context.total_calls == 2); 1923 | buddy_free(buddy, slot); 1924 | assert(context.total_calls == 4); 1925 | } 1926 | 1927 | void test_buddy_tree_init(void) { 1928 | unsigned char buddy_tree_buf[4096]; 1929 | START_TEST; 1930 | assert(buddy_tree_init(buddy_tree_buf, 8) != NULL); 1931 | } 1932 | 1933 | void test_buddy_tree_valid(void) { 1934 | unsigned char buddy_tree_buf[4096]; 1935 | struct buddy_tree *t; 1936 | START_TEST; 1937 | t = buddy_tree_init(buddy_tree_buf, 8); 1938 | assert(!buddy_tree_valid(t, (struct buddy_tree_pos){ 0, 0 })); 1939 | assert(!buddy_tree_valid(t, (struct buddy_tree_pos){ 256, 0 })); 1940 | assert(buddy_tree_valid(t, (struct buddy_tree_pos){ 1, 1 })); 1941 | assert(buddy_tree_valid(t, (struct buddy_tree_pos){ 255, 8 })); 1942 | } 1943 | 1944 | void test_buddy_tree_order(void) { 1945 | unsigned char buddy_tree_buf[4096]; 1946 | struct buddy_tree *t; 1947 | START_TEST; 1948 | t = buddy_tree_init(buddy_tree_buf, 8); 1949 | assert(buddy_tree_order(t) == 8); 1950 | } 1951 | 1952 | void test_buddy_tree_depth(void) { 1953 | START_TEST; 1954 | assert(buddy_tree_depth((struct buddy_tree_pos){ 1, 1 }) == 1); 1955 | assert(buddy_tree_depth((struct buddy_tree_pos){ 2, 2 }) == 2); 1956 | assert(buddy_tree_depth((struct buddy_tree_pos){ 3, 2 }) == 2); 1957 | } 1958 | 1959 | void test_buddy_tree_left_child(void) { 1960 | unsigned char buddy_tree_buf[4096]; 1961 | struct buddy_tree *t; 1962 | struct buddy_tree_pos pos; 1963 | START_TEST; 1964 | t = buddy_tree_init(buddy_tree_buf, 2); 1965 | pos = buddy_tree_root(); 1966 | pos = buddy_tree_left_child(pos); 1967 | assert(buddy_tree_depth(pos) == 2); 1968 | pos = buddy_tree_left_child(pos); 1969 | assert(buddy_tree_valid(t, pos) == 0); 1970 | } 1971 | 1972 | void test_buddy_tree_right_child(void) { 1973 | unsigned char buddy_tree_buf[4096]; 1974 | struct buddy_tree *t; 1975 | struct buddy_tree_pos pos; 1976 | START_TEST; 1977 | t = buddy_tree_init(buddy_tree_buf, 2); 1978 | pos = buddy_tree_root(); 1979 | pos = buddy_tree_right_child(pos); 1980 | assert(buddy_tree_depth(pos) == 2); 1981 | pos = buddy_tree_right_child(pos); 1982 | assert(buddy_tree_valid(t, pos) == 0); 1983 | } 1984 | 1985 | void test_buddy_tree_parent(void) { 1986 | unsigned char buddy_tree_buf[4096]; 1987 | struct buddy_tree *t; 1988 | struct buddy_tree_pos pos; 1989 | START_TEST; 1990 | t = buddy_tree_init(buddy_tree_buf, 2); 1991 | pos = buddy_tree_root(); 1992 | assert(! buddy_tree_valid(t, buddy_tree_parent(pos))); 1993 | assert(! buddy_tree_valid(t, buddy_tree_parent(INVALID_POS))); 1994 | assert(buddy_tree_parent(buddy_tree_left_child(pos)).index == pos.index); 1995 | assert(buddy_tree_parent(buddy_tree_right_child(pos)).index == pos.index); 1996 | } 1997 | 1998 | void test_buddy_tree_right_adjacent(void) { 1999 | unsigned char buddy_tree_buf[4096]; 2000 | struct buddy_tree *t; 2001 | struct buddy_tree_pos pos; 2002 | START_TEST; 2003 | t = buddy_tree_init(buddy_tree_buf, 2); 2004 | pos = buddy_tree_root(); 2005 | assert(! buddy_tree_valid(t, buddy_tree_right_adjacent(pos))); 2006 | assert(! buddy_tree_valid(t, buddy_tree_right_adjacent(buddy_tree_right_child(pos)))); 2007 | assert(buddy_tree_right_adjacent(buddy_tree_left_child(pos)).index == buddy_tree_right_child(pos).index); 2008 | } 2009 | 2010 | void test_buddy_tree_index(void) { 2011 | struct buddy_tree_pos pos = buddy_tree_root(); 2012 | START_TEST; 2013 | assert(buddy_tree_index(pos) == 0); 2014 | assert(buddy_tree_index(buddy_tree_left_child(pos)) == 0); 2015 | assert(buddy_tree_index(buddy_tree_right_child(pos)) == 1); 2016 | } 2017 | 2018 | void test_buddy_tree_mark_status_release_01(void) { 2019 | unsigned char buddy_tree_buf[4096]; 2020 | struct buddy_tree *t; 2021 | struct buddy_tree_pos pos; 2022 | START_TEST; 2023 | t = buddy_tree_init(buddy_tree_buf, 1); 2024 | pos = buddy_tree_root(); 2025 | assert(buddy_tree_status(t, pos) == 0); 2026 | buddy_tree_mark(t, pos); 2027 | assert(buddy_tree_status(t, pos) == 1); 2028 | buddy_tree_release(t, pos); 2029 | assert(buddy_tree_status(t, pos) == 0); 2030 | } 2031 | 2032 | void test_buddy_tree_mark_status_release_02(void) { 2033 | unsigned char buddy_tree_buf[4096]; 2034 | struct buddy_tree *t; 2035 | struct buddy_tree_pos pos; 2036 | START_TEST; 2037 | t = buddy_tree_init(buddy_tree_buf, 2); 2038 | pos = buddy_tree_root(); 2039 | assert(buddy_tree_status(t, pos) == 0); 2040 | buddy_tree_mark(t, pos); 2041 | assert(buddy_tree_status(t, pos) == 2); 2042 | } 2043 | 2044 | void test_buddy_tree_mark_status_release_03(void) { 2045 | unsigned char buddy_tree_buf[4096]; 2046 | struct buddy_tree *t; 2047 | struct buddy_tree_pos pos; 2048 | START_TEST; 2049 | t = buddy_tree_init(buddy_tree_buf, 3); 2050 | pos = buddy_tree_root(); 2051 | assert(buddy_tree_status(t, pos) == 0); 2052 | buddy_tree_mark(t, pos); 2053 | assert(buddy_tree_status(t, pos) == 3); 2054 | } 2055 | 2056 | void test_buddy_tree_mark_status_release_04(void) { 2057 | unsigned char buddy_tree_buf[4096]; 2058 | struct buddy_tree *t; 2059 | struct buddy_tree_pos pos; 2060 | START_TEST; 2061 | t = buddy_tree_init(buddy_tree_buf, 4); 2062 | pos = buddy_tree_root(); 2063 | assert(buddy_tree_status(t, pos) == 0); 2064 | buddy_tree_mark(t, pos); 2065 | assert(buddy_tree_status(t, pos) == 4); 2066 | } 2067 | 2068 | void test_buddy_tree_duplicate_mark(void) { 2069 | unsigned char buddy_tree_buf[4096]; 2070 | struct buddy_tree *t; 2071 | struct buddy_tree_pos pos; 2072 | START_TEST; 2073 | t = buddy_tree_init(buddy_tree_buf, 1); 2074 | pos = buddy_tree_root(); 2075 | buddy_tree_mark(t, pos); 2076 | buddy_tree_mark(t, pos); 2077 | } 2078 | 2079 | void test_buddy_tree_duplicate_free(void) { 2080 | unsigned char buddy_tree_buf[4096]; 2081 | struct buddy_tree *t; 2082 | struct buddy_tree_pos pos; 2083 | START_TEST; 2084 | t = buddy_tree_init(buddy_tree_buf, 1); 2085 | pos = buddy_tree_root(); 2086 | buddy_tree_release(t, pos); 2087 | } 2088 | 2089 | void test_buddy_tree_propagation_01(void) { 2090 | unsigned char buddy_tree_buf[4096]; 2091 | struct buddy_tree *t; 2092 | struct buddy_tree_pos pos, left; 2093 | START_TEST; 2094 | t = buddy_tree_init(buddy_tree_buf, 2); 2095 | pos = buddy_tree_root(); 2096 | left = buddy_tree_left_child(pos); 2097 | assert(buddy_tree_status(t, left) == 0); 2098 | buddy_tree_mark(t, left); 2099 | assert(buddy_tree_status(t, left) == 1); 2100 | assert(buddy_tree_status(t, pos) == 1); 2101 | } 2102 | 2103 | void test_buddy_tree_propagation_02(void) { 2104 | unsigned char buddy_tree_buf[4096]; 2105 | struct buddy_tree *t; 2106 | struct buddy_tree_pos pos, left; 2107 | START_TEST; 2108 | t = buddy_tree_init(buddy_tree_buf, 3); 2109 | pos = buddy_tree_root(); 2110 | left = buddy_tree_left_child(buddy_tree_left_child(pos)); 2111 | buddy_tree_mark(t, left); 2112 | assert(buddy_tree_status(t, left) == 1); 2113 | assert(buddy_tree_status(t, pos) == 1); 2114 | } 2115 | 2116 | void test_buddy_tree_find_free(void) { 2117 | unsigned char buddy_tree_buf[4096]; 2118 | struct buddy_tree *t; 2119 | struct buddy_tree_pos pos; 2120 | START_TEST; 2121 | t = buddy_tree_init(buddy_tree_buf, 3); 2122 | pos = buddy_tree_find_free(t, 1); 2123 | assert(buddy_tree_valid(t, pos) == 1); 2124 | pos = buddy_tree_find_free(t, 2); 2125 | assert(buddy_tree_valid(t, pos) == 1); 2126 | buddy_tree_mark(t, pos); 2127 | pos = buddy_tree_find_free(t, 2); 2128 | assert(buddy_tree_valid(t, pos) == 1); 2129 | buddy_tree_mark(t, pos); 2130 | pos = buddy_tree_find_free(t, 2); 2131 | assert(buddy_tree_valid(t, pos) == 0); 2132 | } 2133 | 2134 | void test_buddy_tree_debug_coverage(void) { 2135 | unsigned char buddy_tree_buf[4096] = {0}; 2136 | struct buddy_tree *t; 2137 | START_TEST; 2138 | t = buddy_tree_init(buddy_tree_buf, 2); 2139 | buddy_tree_mark(t, buddy_tree_root()); 2140 | buddy_tree_debug(t, buddy_tree_root(), 0);printf("\n"); /* code coverage */ 2141 | } 2142 | 2143 | void test_buddy_tree_check_invariant_positive_01(void) { 2144 | unsigned char buddy_tree_buf[4096] = {0}; 2145 | struct buddy_tree *t; 2146 | struct internal_position root_internal; 2147 | START_TEST; 2148 | t = buddy_tree_init(buddy_tree_buf, 2); 2149 | root_internal = buddy_tree_internal_position_tree(t, buddy_tree_root()); 2150 | write_to_internal_position(t, root_internal, 1); 2151 | assert(buddy_tree_check_invariant(t, buddy_tree_root())); 2152 | } 2153 | 2154 | void test_buddy_tree_check_invariant_positive_02(void) { 2155 | unsigned char buddy_tree_buf[4096] = {0}; 2156 | struct buddy_tree *t; 2157 | struct internal_position left_internal; 2158 | START_TEST; 2159 | t = buddy_tree_init(buddy_tree_buf, 2); 2160 | left_internal = buddy_tree_internal_position_tree(t, buddy_tree_left_child(buddy_tree_root())); 2161 | write_to_internal_position(t, left_internal, 1); 2162 | assert(buddy_tree_check_invariant(t, buddy_tree_root())); 2163 | } 2164 | 2165 | void test_buddy_tree_check_invariant_negative_01(void) { 2166 | unsigned char buddy_tree_buf[4096] = {0}; 2167 | struct buddy_tree *t; 2168 | START_TEST; 2169 | t = buddy_tree_init(buddy_tree_buf, 2); 2170 | buddy_tree_mark(t, buddy_tree_root()); 2171 | assert(! buddy_tree_check_invariant(t, buddy_tree_root())); 2172 | } 2173 | 2174 | void test_buddy_tree_check_invariant_negative_02(void) { 2175 | unsigned char buddy_tree_buf[4096] = {0}; 2176 | struct buddy_tree *t; 2177 | START_TEST; 2178 | t = buddy_tree_init(buddy_tree_buf, 2); 2179 | buddy_tree_mark(t, buddy_tree_left_child(buddy_tree_root())); 2180 | assert(! buddy_tree_check_invariant(t, buddy_tree_root())); 2181 | } 2182 | 2183 | void test_buddy_tree_resize_same_size(void) { 2184 | unsigned char buddy_tree_buf[4096] = {0}; 2185 | struct buddy_tree *t; 2186 | START_TEST; 2187 | t = buddy_tree_init(buddy_tree_buf, 1); 2188 | buddy_tree_resize(t, 1); 2189 | } 2190 | 2191 | void test_buddy_tree_resize_01(void) { 2192 | unsigned char buddy_tree_buf[4096] = {0}; 2193 | struct buddy_tree *t; 2194 | START_TEST; 2195 | t = buddy_tree_init(buddy_tree_buf, 1); 2196 | buddy_tree_mark(t, buddy_tree_root()); 2197 | buddy_tree_resize(t, 2); 2198 | assert(buddy_tree_order(t) == 2); 2199 | assert(buddy_tree_status(t, buddy_tree_root()) == 1); 2200 | assert(buddy_tree_status(t, buddy_tree_left_child(buddy_tree_root())) == 1); 2201 | assert(buddy_tree_status(t, buddy_tree_right_child(buddy_tree_root())) == 0); 2202 | buddy_tree_resize(t, 3); 2203 | assert(buddy_tree_status(t, buddy_tree_root()) == 1); 2204 | assert(buddy_tree_status(t, buddy_tree_left_child(buddy_tree_root())) == 1); 2205 | assert(buddy_tree_status(t, buddy_tree_right_child(buddy_tree_root())) == 0); 2206 | assert(buddy_tree_status(t, buddy_tree_left_child(buddy_tree_left_child(buddy_tree_root()))) == 1); 2207 | assert(buddy_tree_status(t, buddy_tree_right_child(buddy_tree_left_child(buddy_tree_root()))) == 0); 2208 | assert(buddy_tree_status(t, buddy_tree_left_child(buddy_tree_right_child(buddy_tree_root()))) == 0); 2209 | assert(buddy_tree_status(t, buddy_tree_right_child(buddy_tree_right_child(buddy_tree_root()))) == 0); 2210 | } 2211 | 2212 | void test_buddy_tree_resize_02(void) { 2213 | unsigned char buddy_tree_buf[4096] = {0}; 2214 | struct buddy_tree *t; 2215 | START_TEST; 2216 | t = buddy_tree_init(buddy_tree_buf, 3); 2217 | buddy_tree_mark(t, buddy_tree_left_child(buddy_tree_root())); 2218 | buddy_tree_resize(t, 2); 2219 | assert(buddy_tree_status(t, buddy_tree_root()) == 2); 2220 | assert(buddy_tree_status(t, buddy_tree_left_child(buddy_tree_root())) == 0); 2221 | assert(buddy_tree_status(t, buddy_tree_right_child(buddy_tree_root())) == 0); 2222 | buddy_tree_resize(t, 1); 2223 | assert(buddy_tree_order(t) == 2); /* cannot shrink */ 2224 | assert(buddy_tree_status(t, buddy_tree_root()) == 2); 2225 | assert(buddy_tree_status(t, buddy_tree_left_child(buddy_tree_root())) == 0); 2226 | assert(buddy_tree_status(t, buddy_tree_right_child(buddy_tree_root())) == 0); 2227 | } 2228 | 2229 | void test_buddy_tree_resize_03(void) { 2230 | unsigned char buddy_tree_buf[4096] = {0}; 2231 | struct buddy_tree *t; 2232 | START_TEST; 2233 | t = buddy_tree_init(buddy_tree_buf, 2); 2234 | buddy_tree_mark(t, buddy_tree_right_child(buddy_tree_root())); 2235 | buddy_tree_resize(t, 1); 2236 | assert(buddy_tree_order(t) == 2); 2237 | assert(buddy_tree_status(t, buddy_tree_root()) == 1); 2238 | assert(buddy_tree_status(t, buddy_tree_left_child(buddy_tree_root())) == 0); 2239 | assert(buddy_tree_status(t, buddy_tree_right_child(buddy_tree_root())) == 1); 2240 | } 2241 | 2242 | void test_buddy_tree_resize_04(void) { 2243 | unsigned char buddy_tree_buf[4096] = {0}; 2244 | struct buddy_tree *t; 2245 | START_TEST; 2246 | t = buddy_tree_init(buddy_tree_buf, 1); 2247 | buddy_tree_mark(t, buddy_tree_root()); 2248 | buddy_tree_resize(t, 2); 2249 | assert(buddy_tree_order(t) == 2); 2250 | assert(buddy_tree_status(t, buddy_tree_root()) == 1); 2251 | assert(buddy_tree_status(t, buddy_tree_left_child(buddy_tree_root())) == 1); 2252 | assert(buddy_tree_status(t, buddy_tree_right_child(buddy_tree_root())) == 0); 2253 | } 2254 | 2255 | void test_buddy_tree_resize_05(void) { 2256 | unsigned char buddy_tree_buf[4096] = {0}; 2257 | struct buddy_tree *t; 2258 | START_TEST; 2259 | t = buddy_tree_init(buddy_tree_buf, 1); 2260 | buddy_tree_resize(t, 2); 2261 | assert(buddy_tree_order(t) == 2); 2262 | assert(buddy_tree_status(t, buddy_tree_root()) == 0); 2263 | assert(buddy_tree_status(t, buddy_tree_left_child(buddy_tree_root())) == 0); 2264 | assert(buddy_tree_status(t, buddy_tree_right_child(buddy_tree_root())) == 0); 2265 | } 2266 | 2267 | void test_buddy_tree_leftmost_child_01(void) { 2268 | unsigned char buddy_tree_buf[4096] = {0}; 2269 | struct buddy_tree *t; 2270 | struct buddy_tree_pos leftmost; 2271 | START_TEST; 2272 | t = buddy_tree_init(buddy_tree_buf, 1); 2273 | leftmost = buddy_tree_leftmost_child(t); 2274 | assert(buddy_tree_valid(t, leftmost)); 2275 | assert(leftmost.index == buddy_tree_root().index); 2276 | } 2277 | 2278 | void test_buddy_tree_leftmost_child_02(void) { 2279 | unsigned char buddy_tree_buf[4096] = {0}; 2280 | struct buddy_tree *t; 2281 | struct buddy_tree_pos leftmost; 2282 | START_TEST; 2283 | t = buddy_tree_init(buddy_tree_buf, 2); 2284 | leftmost = buddy_tree_leftmost_child(t); 2285 | assert(buddy_tree_valid(t, leftmost)); 2286 | assert(leftmost.index == buddy_tree_left_child(buddy_tree_root()).index); 2287 | } 2288 | 2289 | void test_buddy_tree_is_free_01(void) { 2290 | unsigned char buddy_tree_buf[4096] = {0}; 2291 | struct buddy_tree *t; 2292 | struct buddy_tree_pos pos; 2293 | START_TEST; 2294 | t = buddy_tree_init(buddy_tree_buf, 3); 2295 | pos = buddy_tree_leftmost_child(t); 2296 | assert(buddy_tree_is_free(t, pos) == 1); 2297 | pos = buddy_tree_right_adjacent(pos); 2298 | assert(buddy_tree_is_free(t, pos) == 1); 2299 | pos = buddy_tree_right_adjacent(pos); 2300 | assert(buddy_tree_is_free(t, pos) == 1); 2301 | pos = buddy_tree_right_adjacent(pos); 2302 | assert(buddy_tree_is_free(t, pos) == 1); 2303 | } 2304 | 2305 | void test_buddy_tree_is_free_02(void) { 2306 | unsigned char buddy_tree_buf[4096] = {0}; 2307 | struct buddy_tree *t; 2308 | struct buddy_tree_pos pos; 2309 | START_TEST; 2310 | t = buddy_tree_init(buddy_tree_buf, 3); 2311 | pos = buddy_tree_leftmost_child(t); 2312 | buddy_tree_mark(t, pos); 2313 | assert(buddy_tree_is_free(t, pos) == 0); 2314 | pos = buddy_tree_right_adjacent(pos); 2315 | assert(buddy_tree_is_free(t, pos) == 1); 2316 | pos = buddy_tree_right_adjacent(pos); 2317 | assert(buddy_tree_is_free(t, pos) == 1); 2318 | pos = buddy_tree_right_adjacent(pos); 2319 | assert(buddy_tree_is_free(t, pos) == 1); 2320 | } 2321 | void test_buddy_tree_is_free_03(void) { 2322 | unsigned char buddy_tree_buf[4096] = {0}; 2323 | struct buddy_tree *t; 2324 | struct buddy_tree_pos pos; 2325 | START_TEST; 2326 | t = buddy_tree_init(buddy_tree_buf, 3); 2327 | pos = buddy_tree_leftmost_child(t); 2328 | buddy_tree_mark(t, buddy_tree_parent(pos)); 2329 | assert(buddy_tree_is_free(t, pos) == 0); 2330 | pos = buddy_tree_right_adjacent(pos); 2331 | assert(buddy_tree_is_free(t, pos) == 0); 2332 | pos = buddy_tree_right_adjacent(pos); 2333 | assert(buddy_tree_is_free(t, pos) == 1); 2334 | pos = buddy_tree_right_adjacent(pos); 2335 | assert(buddy_tree_is_free(t, pos) == 1); 2336 | } 2337 | 2338 | void test_buddy_tree_is_free_04(void) { 2339 | unsigned char buddy_tree_buf[4096] = {0}; 2340 | struct buddy_tree *t; 2341 | struct buddy_tree_pos pos; 2342 | START_TEST; 2343 | t = buddy_tree_init(buddy_tree_buf, 3); 2344 | buddy_tree_mark(t, buddy_tree_root()); 2345 | pos = buddy_tree_leftmost_child(t); 2346 | assert(buddy_tree_is_free(t, pos) == 0); 2347 | pos = buddy_tree_right_adjacent(pos); 2348 | assert(buddy_tree_is_free(t, pos) == 0); 2349 | pos = buddy_tree_right_adjacent(pos); 2350 | assert(buddy_tree_is_free(t, pos) == 0); 2351 | pos = buddy_tree_right_adjacent(pos); 2352 | assert(buddy_tree_is_free(t, pos) == 0); 2353 | } 2354 | 2355 | void test_buddy_tree_interval(void) { 2356 | unsigned char buddy_tree_buf[4096] = {0}; 2357 | struct buddy_tree *t; 2358 | struct buddy_tree_pos pos; 2359 | struct buddy_tree_interval interval; 2360 | START_TEST; 2361 | t = buddy_tree_init(buddy_tree_buf, 3); 2362 | pos = buddy_tree_leftmost_child(t); 2363 | interval = buddy_tree_interval(t, pos); 2364 | assert(interval.from.index == pos.index); 2365 | assert(interval.to.index == pos.index); 2366 | interval = buddy_tree_interval(t, buddy_tree_parent(pos)); 2367 | assert(interval.from.index == pos.index); 2368 | assert(interval.to.index == buddy_tree_right_adjacent(pos).index); 2369 | } 2370 | 2371 | void test_buddy_tree_interval_contains(void) { 2372 | unsigned char buddy_tree_buf[4096] = {0}; 2373 | struct buddy_tree *t; 2374 | struct buddy_tree_pos pos; 2375 | struct buddy_tree_interval interval_low, interval_high; 2376 | START_TEST; 2377 | t = buddy_tree_init(buddy_tree_buf, 3); 2378 | pos = buddy_tree_leftmost_child(t); 2379 | interval_low = buddy_tree_interval(t, pos); 2380 | interval_high = buddy_tree_interval(t, buddy_tree_parent(pos)); 2381 | assert(buddy_tree_interval_contains(interval_low, interval_low) == 1); 2382 | assert(buddy_tree_interval_contains(interval_high, interval_low) == 1); 2383 | assert(buddy_tree_interval_contains(interval_high, interval_high) == 1); 2384 | assert(buddy_tree_interval_contains(interval_low, interval_high) == 0); 2385 | } 2386 | 2387 | void test_buddy_tree_buddy() { 2388 | unsigned char buf[4096] = {0}; 2389 | struct buddy *buddy; 2390 | struct buddy_tree *tree; 2391 | START_TEST; 2392 | buddy = buddy_embed(buf, 4096); 2393 | tree = buddy_tree(buddy); 2394 | assert(buddy_tree_buddy(tree) == buddy); 2395 | } 2396 | 2397 | void test_buddy_tree_fragmentation(void) { 2398 | unsigned char buddy_tree_buf[4096] = {0}; 2399 | struct buddy_tree *t; 2400 | START_TEST; 2401 | t = buddy_tree_init(buddy_tree_buf, 3); 2402 | 2403 | // No fragmentation for empty tree 2404 | assert(buddy_tree_fragmentation(t) == 0.0); 2405 | 2406 | // No fragmentation for full tree either 2407 | buddy_tree_mark(t, buddy_tree_root()); 2408 | assert(buddy_tree_fragmentation(t) == 0.0); 2409 | buddy_tree_release(t, buddy_tree_root()); 2410 | 2411 | // Some fragmentation for partially-allocated tree 2412 | buddy_tree_mark(t, buddy_tree_left_child(buddy_tree_left_child(buddy_tree_root()))); 2413 | assert(buddy_tree_fragmentation(t) == 143); 2414 | } 2415 | 2416 | int main(void) { 2417 | setvbuf(stdout, NULL, _IONBF, 0); 2418 | 2419 | { 2420 | test_highest_bit_position(); 2421 | test_ceiling_power_of_two(); 2422 | test_popcount_byte(); 2423 | } 2424 | 2425 | { 2426 | test_bitset_basic(); 2427 | test_bitset_range(); 2428 | 2429 | test_bitset_shift(); 2430 | test_bitset_shift_invalid(); 2431 | 2432 | test_bitset_debug(); 2433 | } 2434 | 2435 | { 2436 | test_buddy_init_null(); 2437 | test_buddy_init_overlap(); 2438 | test_buddy_misalignment(); 2439 | test_buddy_embed_misalignment(); 2440 | test_buddy_invalid_datasize(); 2441 | 2442 | test_buddy_init(); 2443 | test_buddy_init_virtual_slots(); 2444 | test_buddy_init_non_power_of_two_memory_01(); 2445 | test_buddy_init_non_power_of_two_memory_02(); 2446 | test_buddy_init_non_power_of_two_memory_03(); 2447 | 2448 | test_buddy_resize_noop(); 2449 | test_buddy_resize_up_within_reserved(); 2450 | test_buddy_resize_up_at_reserved(); 2451 | test_buddy_resize_up_after_reserved(); 2452 | test_buddy_resize_down_to_virtual(); 2453 | test_buddy_resize_down_to_virtual_partial(); 2454 | test_buddy_resize_down_within_reserved(); 2455 | test_buddy_resize_down_within_reserved_failure(); 2456 | test_buddy_resize_down_at_reserved(); 2457 | test_buddy_resize_down_before_reserved(); 2458 | test_buddy_resize_down_already_used(); 2459 | test_buddy_resize_multiple(); 2460 | 2461 | test_buddy_resize_embedded_up_within_reserved(); 2462 | test_buddy_resize_embedded_up_at_reserved(); 2463 | test_buddy_resize_embedded_up_after_reserved(); 2464 | test_buddy_resize_embedded_down_within_reserved(); 2465 | test_buddy_resize_embedded_down_within_reserved_failure(); 2466 | test_buddy_resize_embedded_down_at_reserved(); 2467 | test_buddy_resize_embedded_down_before_reserved(); 2468 | test_buddy_resize_embedded_down_already_used(); 2469 | test_buddy_resize_embedded_too_small(); 2470 | test_buddy_resize_embedded_multiple(); 2471 | 2472 | test_buddy_debug(); 2473 | 2474 | test_buddy_can_shrink(); 2475 | test_buddy_arena_size(); 2476 | test_buddy_arena_free_size_01(); 2477 | test_buddy_arena_free_size_02(); 2478 | test_buddy_arena_free_size_03(); 2479 | 2480 | test_buddy_malloc_null(); 2481 | test_buddy_malloc_zero(); 2482 | test_buddy_malloc_larger(); 2483 | 2484 | test_buddy_malloc_basic_01(); 2485 | test_buddy_malloc_basic_02(); 2486 | test_buddy_malloc_basic_03(); 2487 | test_buddy_malloc_basic_04(); 2488 | 2489 | test_buddy_free_coverage(); 2490 | test_buddy_free_alignment(); 2491 | test_buddy_free_invalid_free_01(); 2492 | test_buddy_free_invalid_free_02(); 2493 | test_buddy_free_invalid_free_03(); 2494 | test_buddy_free_invalid_free_04(); 2495 | 2496 | test_buddy_safe_free_coverage(); 2497 | test_buddy_safe_free_alignment(); 2498 | test_buddy_safe_free_invalid_free_01(); 2499 | test_buddy_safe_free_invalid_free_02(); 2500 | test_buddy_safe_free_invalid_free_03(); 2501 | test_buddy_safe_free_invalid_free_04(); 2502 | test_buddy_safe_free_invalid_free_05(); 2503 | test_buddy_safe_free_invalid_free_06(); 2504 | test_buddy_safe_free_invalid_free_07(); 2505 | 2506 | test_buddy_demo(); 2507 | test_buddy_demo_embedded(); 2508 | 2509 | test_buddy_calloc(); 2510 | test_buddy_calloc_no_members(); 2511 | test_buddy_calloc_no_size(); 2512 | test_buddy_calloc_overflow(); 2513 | 2514 | test_buddy_realloc_01(); 2515 | test_buddy_realloc_02(); 2516 | test_buddy_realloc_03(); 2517 | test_buddy_realloc_04(); 2518 | test_buddy_realloc_05(); 2519 | test_buddy_realloc_06(); 2520 | test_buddy_realloc_07(); 2521 | test_buddy_realloc_08(); 2522 | test_buddy_realloc_alignment(); 2523 | 2524 | test_buddy_realloc_ignore_01(); 2525 | 2526 | test_buddy_reallocarray_01(); 2527 | test_buddy_reallocarray_02(); 2528 | test_buddy_reallocarray_03(); 2529 | 2530 | test_buddy_embedded_not_enough_memory(); 2531 | test_buddy_embedded_null(); 2532 | test_buddy_embedded_01(); 2533 | test_buddy_embedded_malloc_01(); 2534 | test_buddy_embedded_malloc_alignment(); 2535 | 2536 | test_buddy_embed_at(); 2537 | 2538 | test_buddy_mixed_use_01(); 2539 | test_buddy_mixed_use_02(); 2540 | test_buddy_mixed_use_03(); 2541 | 2542 | test_buddy_large_arena(); 2543 | 2544 | test_buddy_walk_01(); 2545 | test_buddy_walk_02(); 2546 | test_buddy_walk_03(); 2547 | test_buddy_walk_04(); 2548 | test_buddy_walk_05(); 2549 | test_buddy_walk_06(); 2550 | 2551 | test_buddy_reserve_01(); 2552 | test_buddy_reserve_02(); 2553 | test_buddy_reserve_03(); 2554 | test_buddy_reserve_04(); 2555 | test_buddy_reserve_05(); 2556 | test_buddy_reserve_coverage(); 2557 | 2558 | test_buddy_unsafe_release_01(); 2559 | test_buddy_unsafe_release_02(); 2560 | 2561 | test_buddy_fragmentation(); 2562 | 2563 | test_buddy_is_empty(); 2564 | test_buddy_is_full(); 2565 | 2566 | test_buddy_slot_alignment(); 2567 | test_buddy_invalid_slot_alignment(); 2568 | 2569 | test_buddy_change_tracking(); 2570 | } 2571 | 2572 | { 2573 | test_buddy_tree_init(); 2574 | test_buddy_tree_valid(); 2575 | test_buddy_tree_order(); 2576 | test_buddy_tree_depth(); 2577 | test_buddy_tree_left_child(); 2578 | test_buddy_tree_right_child(); 2579 | test_buddy_tree_parent(); 2580 | test_buddy_tree_right_adjacent(); 2581 | test_buddy_tree_index(); 2582 | test_buddy_tree_mark_status_release_01(); 2583 | test_buddy_tree_mark_status_release_02(); 2584 | test_buddy_tree_mark_status_release_03(); 2585 | test_buddy_tree_mark_status_release_04(); 2586 | test_buddy_tree_duplicate_mark(); 2587 | test_buddy_tree_duplicate_free(); 2588 | test_buddy_tree_propagation_01(); 2589 | test_buddy_tree_propagation_02(); 2590 | test_buddy_tree_find_free(); 2591 | test_buddy_tree_debug_coverage(); 2592 | test_buddy_tree_check_invariant_positive_01(); 2593 | test_buddy_tree_check_invariant_positive_02(); 2594 | test_buddy_tree_check_invariant_negative_01(); 2595 | test_buddy_tree_check_invariant_negative_02(); 2596 | test_buddy_tree_resize_same_size(); 2597 | test_buddy_tree_resize_01(); 2598 | test_buddy_tree_resize_02(); 2599 | test_buddy_tree_resize_03(); 2600 | test_buddy_tree_resize_04(); 2601 | test_buddy_tree_resize_05(); 2602 | test_buddy_tree_leftmost_child_01(); 2603 | test_buddy_tree_leftmost_child_02(); 2604 | test_buddy_tree_is_free_01(); 2605 | test_buddy_tree_is_free_02(); 2606 | test_buddy_tree_is_free_03(); 2607 | test_buddy_tree_is_free_04(); 2608 | test_buddy_tree_interval(); 2609 | test_buddy_tree_interval_contains(); 2610 | test_buddy_tree_buddy(); 2611 | test_buddy_tree_fragmentation(); 2612 | } 2613 | return 0; 2614 | } 2615 | --------------------------------------------------------------------------------