diff --git a/.github/copilot-instructions.md b/.github/copilot-instructions.md index bb47713..7f941f3 100644 --- a/.github/copilot-instructions.md +++ b/.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. diff --git a/README.md b/README.md index fce7fcc..c5ccb36 100644 --- a/README.md +++ b/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: diff --git a/examples/birth-date-finder/src/main.zig b/examples/birth-date-finder/src/main.zig index c7d6b22..af4ef4f 100644 --- a/examples/birth-date-finder/src/main.zig +++ b/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); - } - } } diff --git a/examples/birth-date-finder/src/plain_text_example.zig b/examples/birth-date-finder/src/plain_text_example.zig index 392431e..539c902 100644 --- a/examples/birth-date-finder/src/plain_text_example.zig +++ b/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); - } - } } diff --git a/examples/birth-date-finder/src/test_multiple.zig b/examples/birth-date-finder/src/test_multiple.zig index 41e2351..4cbcd1e 100644 --- a/examples/birth-date-finder/src/test_multiple.zig +++ b/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); - } - } } } diff --git a/src/pi_finder.zig b/src/pi_finder.zig index cb582af..f1e6120 100644 --- a/src/pi_finder.zig +++ b/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, }; } }