Browse Source

feat: implement automatic memory management and custom JSON serialization

- Add deinit() method to SearchResult for automatic memory management
- Implement custom jsonStringify() method to exclude allocator from JSON output
- Store allocator internally in SearchResult for automatic cleanup
- Simplify API: result.deinit() no longer requires allocator parameter
- Update all examples to use the new simplified API
- Remove dependency on external JSON formatting functions
- Update README.md with new usage patterns and Key Features section
- Fix JSON serialization errors in Zig 0.14.1
- All examples and tests now build and run successfully

Breaking changes:
- SearchResult.deinit() signature changed from deinit(allocator) to deinit()
- Removed formatSearchResultAsJson() and formatSearchResultAsPlainText() functions
- Users should now use std.json.stringify() directly with SearchResult
main
Bastian (BaM) 7 months ago
parent
commit
60cc17f66d
  1. 4
      .github/copilot-instructions.md
  2. 81
      README.md
  3. 16
      examples/birth-date-finder/src/main.zig
  4. 8
      examples/birth-date-finder/src/plain_text_example.zig
  5. 8
      examples/birth-date-finder/src/test_multiple.zig
  6. 48
      src/pi_finder.zig

4
.github/copilot-instructions.md

@ -33,3 +33,7 @@
- The Pi-Finder library is focused on simplicity and performance
- JSON is the standard output format
- Any new features should maintain backward compatibility
## Git Usage
- Always use "git --no-pager" with commands that produce output, such as "git log", "git diff", etc.

81
README.md

@ -5,11 +5,13 @@ A Zig library for searching numeric sequences in the decimal places of Pi.
## Features
- Efficient searching of numeric sequences in Pi decimal places
- JSON output as standard
- Optional plain text output format
- **Automatic memory management** with `result.deinit()`
- **Custom JSON serialization** using `std.json.stringify`
- JSON output as standard format
- Configurable context display with delimiters
- Performance metrics included in results
- Reusable module design
- Performance metrics
- Compatible with Zig 0.14.1
## Installation
@ -59,27 +61,74 @@ pub fn main() !void {
.position = 25,
.delimiter = "-",
.delimiter_interval = 2,
.prefix = "[",
.suffix = "]",
};
// Search for sequence
const result = try pi_finder.searchSequence("123456", context_config, allocator);
defer result.deinit(); // Automatically frees allocated memory
// Output as JSON (default format)
const json_output = try PiFinder.formatSearchResultAsJson(allocator, result);
defer allocator.free(json_output);
// Output as JSON using std.json.stringify
var json_string = std.ArrayList(u8).init(allocator);
defer json_string.deinit();
std.debug.print("{s}\n", .{json_output});
try std.json.stringify(result, .{}, json_string.writer());
std.debug.print("{s}\n", .{json_string.items});
}
```
### Using Plain Text Output Format
### Plain Text Output Example
```zig
// After getting the search result
const plain_text_output = try PiFinder.formatSearchResultAsPlainText(allocator, result);
defer allocator.free(plain_text_output);
if (result.success) {
std.debug.print("Success: {}\n", .{result.success});
std.debug.print("Sequence: {s}\n", .{result.sequence});
if (result.position) |pos| {
std.debug.print("Position: {}\n", .{pos});
}
if (result.context) |context| {
std.debug.print("Context: {s}\n", .{context});
}
} else {
std.debug.print("Search failed: {s}\n", .{result.error_message orelse "Unknown error"});
}
```
## Key Features
### Automatic Memory Management
The library uses RAII (Resource Acquisition Is Initialization) pattern:
```zig
const result = try pi_finder.searchSequence("123456", context_config, allocator);
defer result.deinit(); // Automatically frees allocated context memory
```
### Custom JSON Serialization
SearchResult implements custom JSON serialization that excludes internal fields:
std.debug.print("{s}\n", .{plain_text_output});
```zig
// The allocator field is automatically excluded from JSON output
try std.json.stringify(result, .{}, writer);
```
### Performance Metrics
Every search result includes detailed performance metrics:
```json
{
"performance": {
"file_load_time_ms": 327.988,
"search_time_ms": 10.766,
"file_size_mb": 953.674,
"load_speed_mbs": 2948.052
}
}
```
## API
@ -92,11 +141,6 @@ std.debug.print("{s}\n", .{plain_text_output});
- `getContext(position, config, sequence, allocator)` - Generates context
- `searchSequence(sequence, config, allocator)` - Comprehensive search with structured result
### Output Format Functions
- `formatSearchResultAsJson(allocator, result)` - Formats search result as JSON
- `formatSearchResultAsPlainText(allocator, result)` - Formats search result as plain text
### ContextConfig
Configuration for context display:
@ -120,6 +164,11 @@ Structured result containing:
- `performance` - Performance metrics
- `context_config` - Used context configuration
**Methods:**
- `deinit()` - Frees any dynamically allocated memory (like context)
- `jsonStringify(writer)` - Custom JSON serialization (automatically used by `std.json.stringify`)
## Examples
See the `examples/` directory for complete examples:

16
examples/birth-date-finder/src/main.zig

@ -1,5 +1,5 @@
const std = @import("std");
const PiFinder = @import("pi-finder");
const pi = @import("pi-finder");
pub fn main() !void {
var gpa = std.heap.GeneralPurposeAllocator(.{}){};
@ -10,7 +10,7 @@ pub fn main() !void {
const pi_file_path = "../../data/pi-decimal-10_000_000_000.txt";
// Initialize Pi Finder (JSON output is standard)
var pi_finder = PiFinder.PiFinder.init(allocator, pi_file_path) catch |err| {
var pi_finder = pi.PiFinder.init(allocator, pi_file_path) catch |err| {
std.debug.print("Error initializing Pi finder: {}\n", .{err});
return;
};
@ -20,7 +20,7 @@ pub fn main() !void {
const birth_date = "010102"; // 01.01.02
// Context configuration with delimiter '-'
const context_config = PiFinder.ContextConfig{
const context_config = pi.ContextConfig{
.size = 50,
.position = 25,
.delimiter = "-",
@ -31,18 +31,12 @@ pub fn main() !void {
// Perform search
const result = try pi_finder.searchSequence(birth_date, context_config, allocator);
defer result.deinit();
// Output result as JSON
const json_output = try PiFinder.formatSearchResultAsJson(allocator, result);
const json_output = try pi.formatSearchResultAsJson(allocator, result);
defer allocator.free(json_output);
const stdout = std.io.getStdOut().writer();
try stdout.print("{s}\n", .{json_output});
// Clean up if context was allocated
if (result.context) |context| {
if (context_config.delimiter != null) {
allocator.free(context);
}
}
}

8
examples/birth-date-finder/src/plain_text_example.zig

@ -33,6 +33,7 @@ pub fn main() !void {
// Perform search
const result = try pi_finder.searchSequence(birth_date, context_config, allocator);
defer result.deinit();
// Output result as plain text
const plain_text_output = try PiFinder.formatSearchResultAsPlainText(allocator, result);
@ -40,11 +41,4 @@ pub fn main() !void {
const stdout = std.io.getStdOut().writer();
try stdout.print("{s}\n", .{plain_text_output});
// Clean up if context was allocated
if (result.context) |context| {
if (context_config.delimiter != null) {
allocator.free(context);
}
}
}

8
examples/birth-date-finder/src/test_multiple.zig

@ -41,6 +41,7 @@ pub fn main() !void {
// Perform search
const result = try pi_finder.searchSequence(birth_date, context_config, allocator);
defer result.deinit();
// Output result as JSON
const json_output = try PiFinder.formatSearchResultAsJson(allocator, result);
@ -48,12 +49,5 @@ pub fn main() !void {
const stdout = std.io.getStdOut().writer();
try stdout.print("Searching for {s}:\n{s}\n\n", .{ birth_date, json_output });
// Clean up if context was allocated
if (result.context) |context| {
if (context_config.delimiter != null) {
allocator.free(context);
}
}
}
}

48
src/pi_finder.zig

@ -22,6 +22,7 @@ pub const SearchResult = struct {
error_message: ?[]const u8 = null,
performance: ?PerformanceMetrics = null,
context_config: ?ContextConfig = null,
allocator: ?std.mem.Allocator = null, // privates Feld, wird nicht serialisiert
pub const PerformanceMetrics = struct {
file_load_time_ms: f64,
@ -29,6 +30,49 @@ pub const SearchResult = struct {
file_size_mb: f64,
load_speed_mbs: f64,
};
/// Frees any dynamically allocated memory in the SearchResult
pub fn deinit(self: *const SearchResult) void {
if (self.allocator) |allocator| {
if (self.context) |context| {
allocator.free(context);
}
}
}
/// Custom JSON Serialisierung - ignoriert das allocator Feld
pub fn jsonStringify(self: @This(), writer: anytype) !void {
try writer.beginObject();
try writer.objectField("success");
try writer.write(self.success);
try writer.objectField("sequence");
try writer.write(self.sequence);
try writer.objectField("position");
try writer.write(self.position);
try writer.objectField("context");
try writer.write(self.context);
if (self.error_message != null) {
try writer.objectField("error_message");
try writer.write(self.error_message);
}
if (self.performance != null) {
try writer.objectField("performance");
try writer.write(self.performance);
}
if (self.context_config != null) {
try writer.objectField("context_config");
try writer.write(self.context_config);
}
try writer.endObject();
}
};
/// PiFinder structure with core functionality for searching and formatting sequences
@ -187,6 +231,7 @@ pub const PiFinder = struct {
.position = null,
.context = null,
.error_message = @errorName(err),
.allocator = allocator,
};
};
@ -202,6 +247,7 @@ pub const PiFinder = struct {
.position = pos,
.context = null,
.error_message = @errorName(err),
.allocator = allocator,
};
};
@ -221,6 +267,7 @@ pub const PiFinder = struct {
.load_speed_mbs = load_speed_mbs,
},
.context_config = context_cfg,
.allocator = allocator,
};
} else {
return SearchResult{
@ -229,6 +276,7 @@ pub const PiFinder = struct {
.position = null,
.context = null,
.error_message = "Sequence not found in Pi",
.allocator = allocator,
};
}
}

Loading…
Cancel
Save