b4n1-boost 0.4.2 → 0.4.4
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +4 -4
- data/core.rb +143 -0
- data/lib/native/linux-arm64/libb4n1_boost.so +0 -0
- data/lib/native/linux-x64/libb4n1_boost.so +0 -0
- data/lib/native/macos-arm64/libb4n1_boost.dylib +0 -0
- data/lib/native/macos-x64/libb4n1_boost.dylib +0 -0
- data/lib/native/windows-x64/b4n1_boost.dll +0 -0
- metadata +7 -6
checksums.yaml
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
---
|
|
2
2
|
SHA256:
|
|
3
|
-
metadata.gz:
|
|
4
|
-
data.tar.gz:
|
|
3
|
+
metadata.gz: b2f63ef0c8953fa639cb8ca11d1a9892c75dabcabd90519e13e0858934ae5448
|
|
4
|
+
data.tar.gz: b16352e42cbd457dc10a4dedcba692c3b08f3d997fe16bfa472f554ca1946389
|
|
5
5
|
SHA512:
|
|
6
|
-
metadata.gz:
|
|
7
|
-
data.tar.gz:
|
|
6
|
+
metadata.gz: 7e43fbdd59218e909db7ba8e184f682dad1955bb71c4f31d62e3c2690e58f87fad743bb4a0b7d547d8f05a492446888e9c209a25831f6e738d4ca9e9a7f36297
|
|
7
|
+
data.tar.gz: 48d53e75e0397aed2472d07382b60d6bab26c61db8acd6d91025ccb4adf091ecf09f5d14cbb12cdd4710bdc6e7f4a8ce110bd85c48118dd1bdcca73e900eb442
|
data/core.rb
ADDED
|
@@ -0,0 +1,143 @@
|
|
|
1
|
+
# B4N1-Boost Ruby SDK — core primitives: response cache, bloom filter,
|
|
2
|
+
# circuit breaker. Business Source License 1.1 (BUSL-1.1).
|
|
3
|
+
# Thin FFI wrapper over the proprietary B4N1 native core. ALL logic (LRU
|
|
4
|
+
# eviction, TTL, bloom hashing, circuit thresholds) lives in the native core
|
|
5
|
+
# and is shared with Python, Node.js, .NET and Java through the same C-ABI —
|
|
6
|
+
# a fix in the core fixes every SDK at once.
|
|
7
|
+
# The native binary is licensed separately under the B4N1 EULA (EULA.txt).
|
|
8
|
+
# Copyright (c) 2026 B4N1. All rights reserved.
|
|
9
|
+
require_relative 'boost'
|
|
10
|
+
|
|
11
|
+
module B4N1
|
|
12
|
+
module Boost
|
|
13
|
+
extern 'uint64_t b4n1_cache_new(int)'
|
|
14
|
+
extern 'int b4n1_cache_get(uint64_t, char*, void**, size_t*, uint16_t*)'
|
|
15
|
+
extern 'int b4n1_cache_set(uint64_t, char*, char*, size_t, uint16_t, uint64_t)'
|
|
16
|
+
extern 'int b4n1_cache_invalidate(uint64_t, char*)'
|
|
17
|
+
extern 'void b4n1_cache_clear(uint64_t)'
|
|
18
|
+
extern 'void b4n1_cache_free(uint64_t)'
|
|
19
|
+
extern 'uint64_t b4n1_bloom_new(uint64_t, uint64_t)'
|
|
20
|
+
extern 'int b4n1_bloom_check_and_set(uint64_t, char*)'
|
|
21
|
+
extern 'void b4n1_bloom_free(uint64_t)'
|
|
22
|
+
extern 'uint64_t b4n1_circuit_new(int, int, uint64_t)'
|
|
23
|
+
extern 'int b4n1_circuit_allows(uint64_t)'
|
|
24
|
+
extern 'void b4n1_circuit_record_success(uint64_t)'
|
|
25
|
+
extern 'void b4n1_circuit_record_failure(uint64_t)'
|
|
26
|
+
extern 'void b4n1_circuit_free(uint64_t)'
|
|
27
|
+
|
|
28
|
+
# Response cache (LRU + TTL) backed by the native core.
|
|
29
|
+
#
|
|
30
|
+
# cache = B4N1::Boost::ResponseCache.new(1024)
|
|
31
|
+
# cache.set('user:1', body, 200, 60) # ttl_secs 0 = never expires
|
|
32
|
+
# hit = cache.get('user:1') # => { body:, status: } or nil
|
|
33
|
+
# hit[:body] # binary-safe String
|
|
34
|
+
class ResponseCache
|
|
35
|
+
def initialize(capacity = 1024)
|
|
36
|
+
@handle = Boost.b4n1_cache_new(capacity)
|
|
37
|
+
raise ArgumentError, 'Response cache capacity out of bounds (max 1_000_000)' if @handle.zero?
|
|
38
|
+
|
|
39
|
+
@handle
|
|
40
|
+
end
|
|
41
|
+
|
|
42
|
+
# Returns { body:, status: } on a hit, nil on a miss.
|
|
43
|
+
def get(key)
|
|
44
|
+
out_ptr = Fiddle::Pointer.malloc(Fiddle::SIZEOF_VOIDP)
|
|
45
|
+
out_len = Fiddle::Pointer.malloc(Fiddle::SIZEOF_SIZE_T)
|
|
46
|
+
out_status = Fiddle::Pointer.malloc(2)
|
|
47
|
+
return nil unless Boost.b4n1_cache_get(@handle, key, out_ptr, out_len, out_status) == 1
|
|
48
|
+
|
|
49
|
+
addr = out_ptr[0, Fiddle::SIZEOF_VOIDP].unpack1('J')
|
|
50
|
+
len = out_len[0, Fiddle::SIZEOF_SIZE_T].unpack1('J')
|
|
51
|
+
# Copy before releasing: the core owns that buffer.
|
|
52
|
+
body = Fiddle::Pointer.new(addr).to_str(len)
|
|
53
|
+
Boost.b4n1_boost_free_buffer(Fiddle::Pointer.new(addr), len)
|
|
54
|
+
{ body: body, status: out_status[0, 2].unpack1('S') }
|
|
55
|
+
end
|
|
56
|
+
|
|
57
|
+
# ttl_secs == 0 means never expires (core semantics, matches Node).
|
|
58
|
+
def set(key, body, status = 200, ttl_secs = 300)
|
|
59
|
+
bytes = body.to_s.b
|
|
60
|
+
rc = Boost.b4n1_cache_set(@handle, key, bytes.empty? ? nil : bytes, bytes.bytesize, status, ttl_secs)
|
|
61
|
+
raise 'Response cache set failed (stale handle?)' unless rc.zero?
|
|
62
|
+
end
|
|
63
|
+
|
|
64
|
+
# Returns true when the key existed and was removed.
|
|
65
|
+
def invalidate(key)
|
|
66
|
+
Boost.b4n1_cache_invalidate(@handle, key) == 1
|
|
67
|
+
end
|
|
68
|
+
|
|
69
|
+
def clear
|
|
70
|
+
Boost.b4n1_cache_clear(@handle)
|
|
71
|
+
end
|
|
72
|
+
|
|
73
|
+
def close
|
|
74
|
+
return if @handle.nil? || @handle.zero?
|
|
75
|
+
|
|
76
|
+
Boost.b4n1_cache_free(@handle)
|
|
77
|
+
@handle = 0
|
|
78
|
+
end
|
|
79
|
+
end
|
|
80
|
+
|
|
81
|
+
# Bloom filter for rate limiting / dedup.
|
|
82
|
+
#
|
|
83
|
+
# seen = B4N1::Boost::BloomFilter.new(100_000, 0.01)
|
|
84
|
+
# seen.check_and_set('req-1') # => false (first time, now recorded)
|
|
85
|
+
# seen.check_and_set('req-1') # => true (duplicate)
|
|
86
|
+
class BloomFilter
|
|
87
|
+
def initialize(expected_items = 10_000, fp_rate = 0.01)
|
|
88
|
+
# The core reads the double from its u64 bit pattern.
|
|
89
|
+
bits = [fp_rate.to_f].pack('d').unpack1('Q')
|
|
90
|
+
@handle = Boost.b4n1_bloom_new(expected_items, bits)
|
|
91
|
+
raise ArgumentError, 'Bloom expected items out of bounds (max 10_000_000)' if @handle.zero?
|
|
92
|
+
end
|
|
93
|
+
|
|
94
|
+
# Returns true when the item was already present (duplicate).
|
|
95
|
+
def check_and_set(item)
|
|
96
|
+
Boost.b4n1_bloom_check_and_set(@handle, item) == 1
|
|
97
|
+
end
|
|
98
|
+
|
|
99
|
+
def close
|
|
100
|
+
return if @handle.nil? || @handle.zero?
|
|
101
|
+
|
|
102
|
+
Boost.b4n1_bloom_free(@handle)
|
|
103
|
+
@handle = 0
|
|
104
|
+
end
|
|
105
|
+
end
|
|
106
|
+
|
|
107
|
+
# Circuit breaker: allows? is false while the circuit is open.
|
|
108
|
+
#
|
|
109
|
+
# breaker = B4N1::Boost::CircuitBreaker.new(5, 2, 30)
|
|
110
|
+
# fallback unless breaker.allows?
|
|
111
|
+
# breaker.record_failure # or record_success after a good call
|
|
112
|
+
class CircuitBreaker
|
|
113
|
+
def initialize(threshold = 5, success_threshold = 1, timeout_secs = 30)
|
|
114
|
+
@handle = Boost.b4n1_circuit_new(threshold, success_threshold, timeout_secs)
|
|
115
|
+
raise 'Circuit breaker handle invalid' if @handle.zero?
|
|
116
|
+
end
|
|
117
|
+
|
|
118
|
+
# Returns false when calls must NOT go through (circuit open, or the
|
|
119
|
+
# breaker was closed — a closed breaker must fail CLOSED, never
|
|
120
|
+
# resurrect through the C-ABI's 0-handle legacy path).
|
|
121
|
+
def allows?
|
|
122
|
+
return false if @handle.nil? || @handle.zero?
|
|
123
|
+
|
|
124
|
+
Boost.b4n1_circuit_allows(@handle) == 1
|
|
125
|
+
end
|
|
126
|
+
|
|
127
|
+
def record_success
|
|
128
|
+
Boost.b4n1_circuit_record_success(@handle)
|
|
129
|
+
end
|
|
130
|
+
|
|
131
|
+
def record_failure
|
|
132
|
+
Boost.b4n1_circuit_record_failure(@handle)
|
|
133
|
+
end
|
|
134
|
+
|
|
135
|
+
def close
|
|
136
|
+
return if @handle.nil? || @handle.zero?
|
|
137
|
+
|
|
138
|
+
Boost.b4n1_circuit_free(@handle)
|
|
139
|
+
@handle = 0
|
|
140
|
+
end
|
|
141
|
+
end
|
|
142
|
+
end
|
|
143
|
+
end
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
metadata
CHANGED
|
@@ -1,14 +1,14 @@
|
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
|
2
2
|
name: b4n1-boost
|
|
3
3
|
version: !ruby/object:Gem::Version
|
|
4
|
-
version: 0.4.
|
|
4
|
+
version: 0.4.4
|
|
5
5
|
platform: ruby
|
|
6
6
|
authors:
|
|
7
7
|
- B4N1 Team
|
|
8
|
-
autorequire:
|
|
8
|
+
autorequire:
|
|
9
9
|
bindir: bin
|
|
10
10
|
cert_chain: []
|
|
11
|
-
date: 2026-
|
|
11
|
+
date: 2026-11-04 00:00:00.000000000 Z
|
|
12
12
|
dependencies:
|
|
13
13
|
- !ruby/object:Gem::Dependency
|
|
14
14
|
name: rake
|
|
@@ -36,6 +36,7 @@ files:
|
|
|
36
36
|
- LICENSE
|
|
37
37
|
- README.md
|
|
38
38
|
- boost.rb
|
|
39
|
+
- core.rb
|
|
39
40
|
- lib/native/linux-arm64/libb4n1_boost.so
|
|
40
41
|
- lib/native/linux-x64/libb4n1_boost.so
|
|
41
42
|
- lib/native/macos-arm64/libb4n1_boost.dylib
|
|
@@ -46,7 +47,7 @@ homepage: https://github.com/B4N1-com/private-repos
|
|
|
46
47
|
licenses:
|
|
47
48
|
- BUSL-1.1
|
|
48
49
|
metadata: {}
|
|
49
|
-
post_install_message:
|
|
50
|
+
post_install_message:
|
|
50
51
|
rdoc_options: []
|
|
51
52
|
require_paths:
|
|
52
53
|
- "."
|
|
@@ -61,8 +62,8 @@ required_rubygems_version: !ruby/object:Gem::Requirement
|
|
|
61
62
|
- !ruby/object:Gem::Version
|
|
62
63
|
version: '0'
|
|
63
64
|
requirements: []
|
|
64
|
-
rubygems_version: 3.
|
|
65
|
-
signing_key:
|
|
65
|
+
rubygems_version: 3.4.20
|
|
66
|
+
signing_key:
|
|
66
67
|
specification_version: 4
|
|
67
68
|
summary: High-performance native Rust boost layer for Ruby & Rack/Rails.
|
|
68
69
|
test_files: []
|