Skip to content

Commit 36a593f

Browse files
author
theseyan
committed
initial commit
0 parents  commit 36a593f

File tree

7 files changed

+298
-0
lines changed

7 files changed

+298
-0
lines changed

.gitignore

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,3 @@
1+
.vscode/
2+
.zig-cache/
3+
zig-out/

LICENSE.md

Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,21 @@
1+
MIT License
2+
3+
Copyright (c) 2024-2025 Sayan Jyoti Das
4+
5+
Permission is hereby granted, free of charge, to any person obtaining a copy
6+
of this software and associated documentation files (the "Software"), to deal
7+
in the Software without restriction, including without limitation the rights
8+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9+
copies of the Software, and to permit persons to whom the Software is
10+
furnished to do so, subject to the following conditions:
11+
12+
The above copyright notice and this permission notice shall be included in all
13+
copies or substantial portions of the Software.
14+
15+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21+
SOFTWARE.

README.md

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,12 @@
1+
# mimalloc.zig
2+
3+
A simple implementation of Zig's `std.mem.Allocator` interface over the excellent [mimalloc](https://github.com/microsoft/mimalloc) by Microsoft.
4+
5+
## Usage
6+
7+
```
8+
const mimalloc = @import("mimalloc").Allocator{};
9+
const allocator = mimalloc.allocator();
10+
11+
// Use `allocator` here...
12+
```

build.zig

Lines changed: 79 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,79 @@
1+
const std = @import("std");
2+
3+
pub fn build(b: *std.Build) void {
4+
const target = b.standardTargetOptions(.{});
5+
const optimize = b.standardOptimizeOption(.{});
6+
7+
const lib = b.addModule("mimalloc", .{
8+
.root_source_file = b.path("src/mimalloc.zig"),
9+
.link_libc = true
10+
});
11+
12+
// mimalloc C library
13+
const mimalloc = b.dependency("mimalloc_c", .{});
14+
15+
lib.addIncludePath(mimalloc.path("include"));
16+
17+
// macOS SDK for cross-compilation
18+
const macos_sdk = b.dependency("macos_sdk", .{});
19+
20+
// Add macOS SDK headers if applicable
21+
if (target.result.os.tag == .macos) {
22+
lib.addSystemIncludePath(macos_sdk.path("include"));
23+
}
24+
25+
var c_flags = std.ArrayList([]const u8).init(b.allocator);
26+
if (optimize != .Debug) {
27+
c_flags.append("-DNDEBUG=1") catch unreachable;
28+
c_flags.append("-DMI_SECURE=0") catch unreachable;
29+
c_flags.append("-DMI_STAT=0") catch unreachable;
30+
}
31+
32+
var sources = std.ArrayList([]const u8).init(b.allocator);
33+
sources.appendSlice(&.{
34+
"src/alloc.c",
35+
"src/alloc-aligned.c",
36+
"src/page.c",
37+
"src/heap.c",
38+
"src/random.c",
39+
"src/options.c",
40+
"src/bitmap.c",
41+
"src/os.c",
42+
"src/init.c",
43+
"src/segment.c",
44+
"src/segment-map.c",
45+
"src/arena.c",
46+
"src/stats.c",
47+
"src/prim/prim.c",
48+
"src/libc.c",
49+
}) catch unreachable;
50+
51+
for (sources.items) |src| {
52+
lib.addCSourceFile(.{
53+
.file = mimalloc.path(src),
54+
.flags = c_flags.items,
55+
});
56+
}
57+
58+
if (target.result.os.tag != .windows) {
59+
lib.addCSourceFile(.{
60+
.file = mimalloc.path("src/alloc-posix.c"),
61+
.flags = c_flags.items,
62+
});
63+
}
64+
65+
// Tests
66+
const lib_unit_tests = b.addTest(.{
67+
.root_source_file = b.path("src/test.zig"),
68+
.target = target,
69+
.optimize = optimize,
70+
});
71+
72+
lib_unit_tests.root_module.addImport("mimalloc", lib);
73+
lib_unit_tests.linkLibC();
74+
75+
const run_lib_unit_tests = b.addRunArtifact(lib_unit_tests);
76+
77+
const test_step = b.step("test", "Run unit tests");
78+
test_step.dependOn(&run_lib_unit_tests.step);
79+
}

build.zig.zon

Lines changed: 22 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,22 @@
1+
.{
2+
.name = "mimalloc.zig",
3+
.version = "0.1.0",
4+
.minimum_zig_version = "0.13.0",
5+
6+
.dependencies = .{
7+
.macos_sdk = .{
8+
.url = "https://github.com/mitchellh/zig-build-macos-sdk/archive/refs/heads/main.tar.gz",
9+
.hash = "12209cc9ee372456eda52b71cf9ae77dcc707fa42c9f9d68996b5bf7495b53229c2e"
10+
},
11+
.mimalloc_c = . {
12+
.url = "https://github.com/microsoft/mimalloc/archive/refs/tags/v2.1.7.tar.gz",
13+
.hash = "12209bba1fc4faac01d1ee247a38bdb9f340ce60e457d194fa9da4f7c22b96986521"
14+
}
15+
},
16+
17+
.paths = .{
18+
"build.zig",
19+
"build.zig.zon",
20+
"src",
21+
},
22+
}

src/mimalloc.zig

Lines changed: 71 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,71 @@
1+
const std = @import("std");
2+
const log = std.log.scoped(.mimalloc);
3+
4+
const c = @cImport({
5+
@cInclude("mimalloc.h");
6+
});
7+
8+
pub usingnamespace c;
9+
10+
pub fn collect(force: bool) void {
11+
c.mi_collect(force);
12+
}
13+
14+
pub const Allocator = struct {
15+
const vtable = std.mem.Allocator.VTable{
16+
.alloc = alloc,
17+
.resize = resize,
18+
.free = free,
19+
};
20+
21+
pub fn allocator(self: *Allocator) std.mem.Allocator {
22+
return std.mem.Allocator{
23+
.ptr = self,
24+
.vtable = &vtable,
25+
};
26+
}
27+
28+
fn alloc(
29+
ptr: *anyopaque,
30+
len: usize,
31+
log2_align: u8,
32+
ret_addr: usize,
33+
) ?[*]u8 {
34+
_ = ptr;
35+
_ = ret_addr;
36+
const alignment = @as(usize, 1) << @intCast(log2_align);
37+
return @ptrCast(c.mi_malloc_aligned(len, alignment));
38+
}
39+
40+
fn resize(
41+
ptr: *anyopaque,
42+
buf: []u8,
43+
log2_align: u8,
44+
new_len: usize,
45+
ret_addr: usize,
46+
) bool {
47+
_ = ptr;
48+
_ = log2_align;
49+
_ = ret_addr;
50+
if (new_len > buf.len) {
51+
if (c.mi_expand(buf.ptr, new_len)) |_| {
52+
return true;
53+
}
54+
return false;
55+
} else {
56+
return true;
57+
}
58+
}
59+
60+
fn free(
61+
ptr: *anyopaque,
62+
buf: []u8,
63+
log2_align: u8,
64+
ret_addr: usize,
65+
) void {
66+
_ = ptr;
67+
_ = ret_addr;
68+
_ = log2_align;
69+
c.mi_free(buf.ptr);
70+
}
71+
};

src/test.zig

Lines changed: 90 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,90 @@
1+
const std = @import("std");
2+
const testing = std.testing;
3+
const mimalloc = @import("mimalloc").Allocator{};
4+
const allocator = mimalloc.allocator();
5+
6+
test "basic allocation and deallocation" {
7+
// Test simple allocation
8+
const slice = try allocator.alloc(u8, 100);
9+
defer allocator.free(slice);
10+
try testing.expectEqual(@as(usize, 100), slice.len);
11+
12+
// Fill with data to ensure memory is writable
13+
@memset(slice, 0xAA);
14+
for (slice) |byte| {
15+
try testing.expectEqual(@as(u8, 0xAA), byte);
16+
}
17+
}
18+
19+
test "zero-sized allocation" {
20+
// Test allocation of zero bytes
21+
const empty = try allocator.alloc(u8, 0);
22+
defer allocator.free(empty);
23+
try testing.expectEqual(@as(usize, 0), empty.len);
24+
}
25+
26+
test "realloc behavior" {
27+
// Initial allocation
28+
var slice = try allocator.alloc(u8, 50);
29+
defer allocator.free(slice);
30+
31+
// Fill with pattern
32+
@memset(slice, 0xBB);
33+
34+
// Grow
35+
slice = try allocator.realloc(slice, 100);
36+
try testing.expectEqual(@as(usize, 100), slice.len);
37+
38+
// Verify original data is preserved
39+
for (slice[0..50]) |byte| {
40+
try testing.expectEqual(@as(u8, 0xBB), byte);
41+
}
42+
43+
// Shrink
44+
slice = try allocator.realloc(slice, 25);
45+
try testing.expectEqual(@as(usize, 25), slice.len);
46+
47+
// Verify data is still preserved
48+
for (slice) |byte| {
49+
try testing.expectEqual(@as(u8, 0xBB), byte);
50+
}
51+
}
52+
53+
test "multiple allocations" {
54+
var slices = std.ArrayList([]u8).init(testing.allocator);
55+
defer {
56+
for (slices.items) |slice| {
57+
allocator.free(slice);
58+
}
59+
slices.deinit();
60+
}
61+
62+
// Perform multiple allocations of varying sizes
63+
const sizes = [_]usize{ 8, 16, 32, 64, 128, 256, 512, 1024 };
64+
for (sizes) |size| {
65+
const slice = try allocator.alloc(u8, size);
66+
try slices.append(slice);
67+
@memset(slice, @as(u8, @truncate(size % 256)));
68+
}
69+
70+
// Verify all allocations
71+
for (slices.items, 0..) |slice, i| {
72+
try testing.expectEqual(sizes[i], slice.len);
73+
for (slice) |byte| {
74+
try testing.expectEqual(@as(u8, @truncate(sizes[i] % 256)), byte);
75+
}
76+
}
77+
}
78+
79+
test "alignment requirements" {
80+
const AlignedStruct = struct {
81+
a: u32 align(16),
82+
b: u64,
83+
};
84+
85+
var slice = try allocator.alloc(AlignedStruct, 1);
86+
defer allocator.free(slice);
87+
88+
// Verify alignment
89+
try testing.expectEqual(@as(usize, 0), @intFromPtr(&slice[0]) & 15);
90+
}

0 commit comments

Comments
 (0)