Compare commits

...

3 Commits
0.1.0 ... main

Author SHA1 Message Date
Bastian (BaM) 83dbdd2d93 Bump version to 0.1.2 7 months ago
Bastian (BaM) 60cc17f66d feat: implement automatic memory management and custom JSON serialization 7 months ago
Bastian (BaM) 04bbb717c2 Update build.zig.zon 7 months ago
  1. 4
      .github/copilot-instructions.md
  2. 81
      README.md
  3. 84
      build.zig.zon
  4. 16
      examples/birth-date-finder/src/main.zig
  5. 8
      examples/birth-date-finder/src/plain_text_example.zig
  6. 8
      examples/birth-date-finder/src/test_multiple.zig
  7. 48
      src/pi_finder.zig

4
.github/copilot-instructions.md

@ -33,3 +33,7 @@
- The Pi-Finder library is focused on simplicity and performance - The Pi-Finder library is focused on simplicity and performance
- JSON is the standard output format - JSON is the standard output format
- Any new features should maintain backward compatibility - 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 ## Features
- Efficient searching of numeric sequences in Pi decimal places - Efficient searching of numeric sequences in Pi decimal places
- JSON output as standard - **Automatic memory management** with `result.deinit()`
- Optional plain text output format - **Custom JSON serialization** using `std.json.stringify`
- JSON output as standard format
- Configurable context display with delimiters - Configurable context display with delimiters
- Performance metrics included in results
- Reusable module design - Reusable module design
- Performance metrics - Compatible with Zig 0.14.1
## Installation ## Installation
@ -59,27 +61,74 @@ pub fn main() !void {
.position = 25, .position = 25,
.delimiter = "-", .delimiter = "-",
.delimiter_interval = 2, .delimiter_interval = 2,
.prefix = "[",
.suffix = "]",
}; };
// Search for sequence // Search for sequence
const result = try pi_finder.searchSequence("123456", context_config, allocator); const result = try pi_finder.searchSequence("123456", context_config, allocator);
defer result.deinit(); // Automatically frees allocated memory
// Output as JSON (default format) // Output as JSON using std.json.stringify
const json_output = try PiFinder.formatSearchResultAsJson(allocator, result); var json_string = std.ArrayList(u8).init(allocator);
defer allocator.free(json_output); 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 ```zig
// After getting the search result // After getting the search result
const plain_text_output = try PiFinder.formatSearchResultAsPlainText(allocator, result); if (result.success) {
defer allocator.free(plain_text_output); 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 ## API
@ -92,11 +141,6 @@ std.debug.print("{s}\n", .{plain_text_output});
- `getContext(position, config, sequence, allocator)` - Generates context - `getContext(position, config, sequence, allocator)` - Generates context
- `searchSequence(sequence, config, allocator)` - Comprehensive search with structured result - `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 ### ContextConfig
Configuration for context display: Configuration for context display:
@ -120,6 +164,11 @@ Structured result containing:
- `performance` - Performance metrics - `performance` - Performance metrics
- `context_config` - Used context configuration - `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 ## Examples
See the `examples/` directory for complete examples: See the `examples/` directory for complete examples:

84
build.zig.zon

@ -1,23 +1,85 @@
.{ .{
// Basic package information // This is the default name used by packages depending on this one. For
.name = "pi-finder", // example, when a user runs `zig fetch --save <url>`, this field is used
.version = "0.1.0", // as the key in the `dependencies` table. Although the user can choose a
// different name, most users will stick with this provided value.
//
// It is redundant to include "zig" in this name because it is already
// within the Zig package namespace.
.name = .pi_finder,
// Minimum Zig version required for this package // This is a [Semantic Version](https://semver.org/).
// In a future version of Zig it will be used for package deduplication.
.version = "0.1.2",
// Together with name, this represents a globally unique package
// identifier. This field is generated by the Zig toolchain when the
// package is first created, and then *never changes*. This allows
// unambiguous detection of one package being an updated version of
// another.
//
// When forking a Zig project, this id should be regenerated (delete the
// field and run `zig build`) if the upstream project is still maintained.
// Otherwise, the fork is *hostile*, attempting to take control over the
// original project's identity. Thus it is recommended to leave the comment
// on the following line intact, so that it shows up in code reviews that
// modify the field.
.fingerprint = 0x74ba60f053049abd, // Changing this has security and trust implications.
// Tracks the earliest Zig version that the package considers to be a
// supported use case.
.minimum_zig_version = "0.14.1", .minimum_zig_version = "0.14.1",
// Package dependencies (currently none, but could be added later) // This field is optional.
.dependencies = .{}, // Each dependency must either provide a `url` and `hash`, or a `path`.
// `zig build --fetch` can be used to fetch all dependencies of a package, recursively.
// Once all dependencies are fetched, `zig build` no longer requires
// internet connectivity.
.dependencies = .{
// See `zig fetch --save <url>` for a command-line interface for adding dependencies.
//.example = .{
// // When updating this field to a new URL, be sure to delete the corresponding
// // `hash`, otherwise you are communicating that you expect to find the old hash at
// // the new URL. If the contents of a URL change this will result in a hash mismatch
// // which will prevent zig from using it.
// .url = "https://example.com/foo.tar.gz",
//
// // This is computed from the file contents of the directory of files that is
// // obtained after fetching `url` and applying the inclusion rules given by
// // `paths`.
// //
// // This field is the source of truth; packages do not come from a `url`; they
// // come from a `hash`. `url` is just one of many possible mirrors for how to
// // obtain a package matching this `hash`.
// //
// // Uses the [multihash](https://multiformats.io/multihash/) format.
// .hash = "...",
//
// // When this is provided, the package is found in a directory relative to the
// // build root. In this case the package's hash is irrelevant and therefore not
// // computed. This field and `url` are mutually exclusive.
// .path = "foo",
//
// // When this is set to `true`, a package is declared to be lazily
// // fetched. This makes the dependency only get fetched if it is
// // actually used.
// .lazy = false,
//},
},
// Package metadata // Specifies the set of files and directories that are included in this package.
// Only files and directories listed here are included in the `hash` that
// is computed for this package. Only files listed here will remain on disk
// when using the zig package manager. As a rule of thumb, one should list
// files required for compilation plus any license(s).
// Paths are relative to the build root. Use the empty string (`""`) to refer to
// the build root itself.
// A directory listed here means that all files within, recursively, are included.
.paths = .{ .paths = .{
// Include all important project files
"build.zig", "build.zig",
"build.zig.zon", "build.zig.zon",
"src", "src",
"README.md", "README.md",
"LICENSE", "examples",
// Note: we don't include the data directory directly in the package
// as it contains very large files
}, },
} }

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

@ -1,5 +1,5 @@
const std = @import("std"); const std = @import("std");
const PiFinder = @import("pi-finder"); const pi = @import("pi-finder");
pub fn main() !void { pub fn main() !void {
var gpa = std.heap.GeneralPurposeAllocator(.{}){}; 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"; const pi_file_path = "../../data/pi-decimal-10_000_000_000.txt";
// Initialize Pi Finder (JSON output is standard) // 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}); std.debug.print("Error initializing Pi finder: {}\n", .{err});
return; return;
}; };
@ -20,7 +20,7 @@ pub fn main() !void {
const birth_date = "010102"; // 01.01.02 const birth_date = "010102"; // 01.01.02
// Context configuration with delimiter '-' // Context configuration with delimiter '-'
const context_config = PiFinder.ContextConfig{ const context_config = pi.ContextConfig{
.size = 50, .size = 50,
.position = 25, .position = 25,
.delimiter = "-", .delimiter = "-",
@ -31,18 +31,12 @@ pub fn main() !void {
// Perform search // Perform search
const result = try pi_finder.searchSequence(birth_date, context_config, allocator); const result = try pi_finder.searchSequence(birth_date, context_config, allocator);
defer result.deinit();
// Output result as JSON // 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); defer allocator.free(json_output);
const stdout = std.io.getStdOut().writer(); const stdout = std.io.getStdOut().writer();
try stdout.print("{s}\n", .{json_output}); 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 // Perform search
const result = try pi_finder.searchSequence(birth_date, context_config, allocator); const result = try pi_finder.searchSequence(birth_date, context_config, allocator);
defer result.deinit();
// Output result as plain text // Output result as plain text
const plain_text_output = try PiFinder.formatSearchResultAsPlainText(allocator, result); const plain_text_output = try PiFinder.formatSearchResultAsPlainText(allocator, result);
@ -40,11 +41,4 @@ pub fn main() !void {
const stdout = std.io.getStdOut().writer(); const stdout = std.io.getStdOut().writer();
try stdout.print("{s}\n", .{plain_text_output}); 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 // Perform search
const result = try pi_finder.searchSequence(birth_date, context_config, allocator); const result = try pi_finder.searchSequence(birth_date, context_config, allocator);
defer result.deinit();
// Output result as JSON // Output result as JSON
const json_output = try PiFinder.formatSearchResultAsJson(allocator, result); const json_output = try PiFinder.formatSearchResultAsJson(allocator, result);
@ -48,12 +49,5 @@ pub fn main() !void {
const stdout = std.io.getStdOut().writer(); const stdout = std.io.getStdOut().writer();
try stdout.print("Searching for {s}:\n{s}\n\n", .{ birth_date, json_output }); 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, error_message: ?[]const u8 = null,
performance: ?PerformanceMetrics = null, performance: ?PerformanceMetrics = null,
context_config: ?ContextConfig = null, context_config: ?ContextConfig = null,
allocator: ?std.mem.Allocator = null, // privates Feld, wird nicht serialisiert
pub const PerformanceMetrics = struct { pub const PerformanceMetrics = struct {
file_load_time_ms: f64, file_load_time_ms: f64,
@ -29,6 +30,49 @@ pub const SearchResult = struct {
file_size_mb: f64, file_size_mb: f64,
load_speed_mbs: 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 /// PiFinder structure with core functionality for searching and formatting sequences
@ -187,6 +231,7 @@ pub const PiFinder = struct {
.position = null, .position = null,
.context = null, .context = null,
.error_message = @errorName(err), .error_message = @errorName(err),
.allocator = allocator,
}; };
}; };
@ -202,6 +247,7 @@ pub const PiFinder = struct {
.position = pos, .position = pos,
.context = null, .context = null,
.error_message = @errorName(err), .error_message = @errorName(err),
.allocator = allocator,
}; };
}; };
@ -221,6 +267,7 @@ pub const PiFinder = struct {
.load_speed_mbs = load_speed_mbs, .load_speed_mbs = load_speed_mbs,
}, },
.context_config = context_cfg, .context_config = context_cfg,
.allocator = allocator,
}; };
} else { } else {
return SearchResult{ return SearchResult{
@ -229,6 +276,7 @@ pub const PiFinder = struct {
.position = null, .position = null,
.context = null, .context = null,
.error_message = "Sequence not found in Pi", .error_message = "Sequence not found in Pi",
.allocator = allocator,
}; };
} }
} }

Loading…
Cancel
Save